51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

基于C++11的数据库连接池

数据库连接池概述 {#数据库连接池概述}

我们在进行数据库操作的时候为了提高数据库(关系型数据库)的访问瓶颈,除了在服务器端增加缓存服务器(例如redis)缓存常用的数据之外,还可以增加连接池,来提高数据库服务器的访问效率。

一般来说,对于数据库操作都是在访问数据库的时候创建连接,访问完毕断开连接。但是如果在高并发情况下,有些需要频繁处理的操作就会消耗很多的资源和时间,比如:

  1. 建立通信连接的TCP三次握手
  2. 数据库服务器的连接认证
  3. 数据库服务器关闭连接时的资源回收
  4. 断开通信连接的TCP四次挥手

如果使用数据库连接池会减少这一部分的性能损耗。

接下来会基于MySql数据库(使用MySQL的API连接MySQL数据库)为大家讲解一下,如何使用C++11的相关新特性来实现一个数据库连接池。

涉及的技术点 {#涉及的技术点}

  • C++11 新特性

    1. 多线程编程
    2. 线程同步(互斥锁的使用)
    3. 处理时间和日期的 chrono 库
    4. 条件变量
    5. 智能指针
    6. lambda 表达式
    7. 使用 =delete 删除函数
  • 其它知识点

    1. MySQL 数据库编程,主要是官方 API 的封装和使用

      MySQL API 详解

    2. 单例模式

    3. STL容器

    4. 生产者和消费者模型

    5. Jsoncpp库的使用(解析配置文件中的数据库相关信息)

连接池的设计 {#连接池的设计}

要设计一个数据库连接池,我们需要实现以下几个功能点:

  1. 连接池只需要一个实例,所以连接池类应该是一个单例模式的类
  2. 所有的数据库连接应该维护到一个安全的队列中
    • 使用队列的目的是方便连接的添加和删除
    • 所谓的安全指的是线程安全,也就是说需要使用互斥锁来保护队列数据的读写。
  3. 在需要的时候可以从连接池中得到一个或多个可用的数据库连接
    • 如果有可用连接,直接取出
    • 如果没有可用连接,阻塞等待一定时长然后再重试
  4. 如果队列中没有多余的可用连接,需要动态的创建新连接
  5. 如果队列中空闲的连接太多,需要动态的销毁一部分
  6. 数据库操作完毕,需要将连接归还到连接池中

细节分析 {#细节分析}

  1. 数据库连接的存储 :可用使用STL中的队列queue
  2. 连接池连接的动态创建:这部分工作需要交给一个单独的线程来处理
  3. 连接池连接的动态销毁:这部分工作需要交给一个单独的线程来处理
  4. 数据库连接的添加和归还 :这是一个典型的生产者和消费者模型
    • 消费者:需要访问数据库的线程,数据库连接被取出(消费)
    • 生产者:专门负责创建数据库连接的线程
    • 处理生产者和消费者模型需要使用条件变量阻塞线程
  5. 连接池的默认连接数量 :连接池中提供的可用连接的最小数量
    • 如果不够就动态创建
    • 如果太多就动态销毁
  6. 连接池的最大连接数量:能够创建的最大有效数据库连接上限
  7. 最大空闲时间:创建出的数据库连接在指定时间长度内一直未被使用,此时就需要销毁该连接。
  8. 连接超时:消费者线程无法获取到可用连接是,阻塞等待的时间长度

综上所述,数据库连接池对应的单例模式的类的设计如下:

|------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | using namespace std; /* * 数据库连接池: 单例模式 * MySqlConn 是一个连接MySQL数据库的类 */ class ConnectionPool { public: // 得到单例对象 static ConnectionPool* getConnectPool(); // 从连接池中取出一个连接 shared_ptr<MySqlConn> getConnection(); // 删除拷贝构造和拷贝赋值运算符重载函数 ConnectionPool(const ConnectionPool& obj) = delete; ConnectionPool& operator=(const ConnectionPool& obj) = delete; private: // 构造函数私有化 ConnectionPool(); bool parseJsonFile(); void produceConnection(); void recycleConnection(); void addConnection(); string m_ip; // 数据库服务器ip地址 string m_user; // 数据库服务器用户名 string m_dbName; // 数据库服务器的数据库名 string m_passwd; // 数据库服务器密码 unsigned short m_port; // 数据库服务器绑定的端口 int m_minSize; // 连接池维护的最小连接数 int m_maxSize; // 连接池维护的最大连接数 int m_maxIdleTime; // 连接池中连接的最大空闲时长 int m_timeout; // 连接池获取连接的超时时长 queue<MySqlConn*> m_connectionQ; mutex m_mutexQ; condition_variable m_cond; }; |

MySQL环境和代码下载 {#MySQL环境和代码下载}

Windows下载和安装MySql {#Windows下载和安装MySql}

下载并安装MySQL

如果是8.0版本的MySQL在连接数据的时候需要加密,因此需要额外提供对应的动态库,下载方式如下: 下载8.0版本的MySQL链接库

VS配置MySQL环境 {#VS配置MySQL环境}

打开项目的属性窗口,在属性页配置MySQL头文件目录MySQL的库目录

这是我本地安装MySQL之后的目录结构:

在VS项目的属性页窗口指定要加载的动态库对应的导入库(xxx.lib)

我们在调用MySQL API的使用需要加载的动态库为libmysql.dll,它对应的导入库为libmysql.lib,在该窗口的附加依赖项位置指定的就是这个导入库的名字。

编译编写好的项目之后,在对应的项目目录中会生成一个可执行程序,打开这个目录,将上面步骤中下载的用于MySQL数据库加密的动态库拷贝到该目录中,这样程序就可以正常执行了,否则会提示无法加载某个动态库。


关注微信公众号爱编程的大丙,回复数据库连接池 获取MySQL Windows安装包和连接池代码的百度网盘链接。


赞(5)
未经允许不得转载:工具盒子 » 基于C++11的数据库连接池