文章目录

  • 前置知识
    • 关键技术点
    • 项目背景
    • 连接池功能点介绍
    • MySQL Server参数介绍
    • 功能设计
    • 连接池功能点介绍
    • 开发平台选型
  • 关于MySQL数据库编程
    • MySQL接口介绍
  • 测试表设计
  • Connection设计
  • 数据库配置文件mysql.conf
  • 日志文件log.hpp
  • ConnectionPool设计
  • 压力测试
  • 源码链接:

前置知识

关键技术点

MySQL数据库编程、单例模式、queue队列容器、C++11多线程编程、线程互斥、线程同步通信和unique_lock、基于CAS的原子整形、智能指针shared_ptr、lambda表达式、生产者-消费者线程模型

项目背景

MySQL是一个基于C/S设计的关系型数据库管理系统,一条SQL的执行需要通过mysql client发起一个连接,经过TCP三次握手完成TCP连接,然后再对客户端进行身份验证,验证成功后再把SQL发给mysql server(RDBMS)执行SQL(一般会涉及磁盘IO),然后给客户端返回执行结果,执行结束后,进行四次挥手断开连接

如果想要提高MySQL数据库(基于C/S设计)的访问瓶颈:

  • 在服务器端增加缓存服务器,用户缓存常用的数据(例如redis),减少磁盘IO的次数
  • 还可以使用连接池,来提高MySQL Server的访问效率

在高并发情况下:大量的TCP三次握手建立MySQL Server连接认证 以及 TCP四次挥手 MySQL Server关闭连接回收资源所耗费的性能时间也是很明显的,使用连接池就是为了减少这一部分的性能损耗


连接池功能点介绍

该项目是基于C++语言实现的连接池,主要也是实现几个所有连接池都支持的通用基础功能,连接池一般包含了以下内容:

  • 数据库连接所用的ip地址
  • port端口号
  • 用户名和密码,连接哪个库
  • 以及连接池的性能参数,例如 初始连接量,最大连接量,最大空闲时间、连接超时时间等,

1.初始连接量 i n i t S i z e initSize initSize):表示连接池会预先和MySQL Server创建 i n i t S i z e initSize initSize个连接,当用户发起MySQL访问时,不需要创建新的连接,直接从连接池中获取一个可用的连接就可以,使用完成后,并不去释放该连接,而是把当前连接重新归还到连接池当中

2.最大连接量 m a x S i z e maxSize maxSize):当并发访问MySQL Server的请求增多时,初始连接量已经不够使用的时候,此时会根据新的请求数量去创建更多的连接给用户去使用,但是新创建的连接数量上限是 m a x S i z e maxSize maxSize,不能无限制的创建连接。

  • 因为每一个连接都会占用一个 s o c k e t socket socket资源,一般连接池和服务器程序是部署在一台主机上的,如果连接池占用过多的 s o c k e t socket socket资源,那么服务器就不能接收太多的客户端请求了。当这些连接使用完成后,再次归还到连接池当中来维护

3.最大空闲时间 m a x I d l e T i m e maxIdleTime maxIdleTime):当访问MySQL Server的并发请求多了以后,连接池里面的连接数量会动态增加,上限是 m a x S i z e maxSize maxSize个,当这些连接用完再次归还到连接池当中。如果在指定的 m a x I d l e T i m e maxIdleTime maxIdleTime里面,这些新增加的连接都没有被再次使用过,那么新增加的这些连接资源就要被回收掉,只需要保持初始连接量 i n i t S i z e initSize initSize个连接就可以了

4.连接超时时间 c o n n e c t i o n T i m e o u t connectionTimeout connectionTimeout):当MySQL Server的并发请求量过大,连接池中的连接数量已经到达 m a x S i z e maxSize maxSize了,而此时没有空闲的连接可供使用,那么此时用户无法从连接池获取连接,它通过”阻塞”的方式获取连接的时间如果超过 c o n n e c t i o n T i m e o u t connectionTimeout connectionTimeout,那么获取连接失败,无法访问数据库


MySQL Server参数介绍

show variables like 'max_connections';#查看MySQL Server所支持的最大连接个数

超过 m a x _ c o n n e c t i o n s max\_connections max_connections数量的连接,MySQL Server会直接拒绝访问,所以在使用连接池增加连接数量的时候,MySQL Server的 m a x c o n n e c t i o n s max_connections maxconnections参数也要适当的进行调整,以适配连接池的连接上限


功能设计

文件功能
$ConnectionPool.cpp $ 和 C o n n e c t i o n P o o l . h ConnectionPool.h ConnectionPool.h实现连接池
C o n n e c t i o n . c p p Connection.cpp Connection.cpp C o n n e c t i o n . h Connection.h Connection.h描述每一条建立的连接:数据库操作代码、增删改查代码实现

连接池功能点介绍

1.连接池只需要一个实例,所以采用单例模式进行设计

2.服务器一般是多线程,可能多个线程都发起了对这个Mysql Server的操作请求,都需要从队列当中获取连接空闲连接,而 C o n n e c t i o n Connection Connection全部维护在一个连接队列中 => 所以需要保证连接队列的线程安全,需要使用互斥锁保证队列的线程安全

3.如果连接队列为空,还需要再获取连接,此时需要动态创建连接,上限数量是 m a x S i z e maxSize maxSize

4.连接队列中空闲连接时间超过 m a x I d l e T i m e maxIdleTime maxIdleTime的就要被释放掉,只保留初始的 i n i t S i z e initSize initSize个连接就可以了,这个功能点肯定需要放在独立的线程中去做

5.如果连接队列为空,而此时连接的数量已达上限 m a x S i z e maxSize maxSize,客户**“阻塞”**等待 c o n n e c t i o n T i m e o u t connectionTimeout connectionTimeout时间之后还没有获取到空闲的连接,那么获取连接失败

  • 此处”阻塞”从Connection队列获取空闲连接,可以使用带超时时间的mutex互斥锁来实现连接超时时间
  • 假设连接超时时间为100ms => 并不是直接睡眠100ms,而是在这100ms时间内,不断判断是否连接队列当中是否有空闲连接,如果有,那么直接获取队头的连接,否则连接池队列为空,就获取失败

6.用户获取的连接需要用shared_ptr智能指针来管理,需要定制删除器 定制 连接释放的功能(此处并不是真正释放连接,而是把连接归还到连接池中)

7.新连接的生产线程 和 获取连接的线程 采用生产者-消费者线程模型来设计,所以需要使用线程间的同步通信机制来保证 => 条件变量和互斥锁


具体流程

假设我们的服务器给客户提供服务,客户端发起请求需要数据库操作时,Server需要到连接池管理的队列中获取一个连接,然后连接池给Server返回一个智能指针维护的连接,Server只管使用这条连接,无需关心这条连接的释放,然后使用这条连接去访问MySQL Server


开发平台选型

有关MySQL数据库编程、多线程编程、线程互斥和同步通信操作、智能指针、设计模式、容器等等这些技术在C++语言层面都可以直接实现,因此该项目选择直接在windows平台上进行开发

当然,因为采用的都是语言级别的接口,没有强依赖系统的接口,所以跨平台性比较好:放在Linux平台下用g++也可以直接编译运行,但是需要解决一些库的依赖问题


关于MySQL数据库编程

MySQL的windows安装文件云盘地址如下(下载development开发版:包含mysql头文件和libmysql库文件):链接:https://pan.baidu.com/s/1Y1l7qvpdR2clW5OCdOTwrQ提取码:95de

MySQL数据库编程直接采用oracle公司提供的MySQL C/C++客户端开发包,在VS上需要进行相应的头文件和库文件的配置,如下

  • 1.右键项目 – C/C++ – 常规 – 附加包含目录,填写下载好的mysql.h头文件在当前电脑的路径
  • 2.右键项目 – 链接器 – 常规 – 附加库目录,填写libmysql.lib的路径
  • 3.右键项目 – 链接器 – 输入 – 附加依赖项,填写libmysql.lib库的名字
  • 4.把 l i b m y s q l . d l l libmysql.dll libmysql.dll动态链接库(Linux下后缀名是.so库)放在工程目录下

坑点:如果MySQL装的是64位版本的,所以动态库什么的都是64位生成的,所以项目先选成64位的


MySQL接口介绍

初始化:mysql_init()

要使用库,必须先进行初始化 由此也可以看出MySQL其实是网络服务,句柄就是文件描述符

MYSQL *my = mysql_init(nullptr);

链接数据库mysql_real_connect() :

初始化完毕之后,必须先链接数据库,再进行后续操作,因为mysql网络部分是基于TCP/IP的

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,