基于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 使用详解-CSDN博客https://blog.csdn.net/a16675900046/article/details/164063265?sharetype=blogdetail&sharerId=164063265&sharerefer=PC&sharesource=a16675900046&spm=1011.2480.3001.8118

2.单例模式

3.STL容器

4.生产者和消费者模型

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

连接池的设计

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

1.连接池只需要一个实例,所以连接池类应该是一个单例模式的类

2.所有的数据库连接应该维护到一个安全的队列中

使用队列的目的是方便连接的添加和删除

所谓的安全指的是线程安全,也就是说需要使用互斥锁来保护队列数据的读写。

3.在需要的时候可以从连接池中得到一个或多个可用的数据库连接

如果有可用连接,直接取出

如果没有可用连接,阻塞等待一定时长然后再重试

4.如果队列中没有多余的可用连接,需要动态的创建新连接

5.如果队列中空闲的连接太多,需要动态的销毁一部分

6.数据库操作完毕,需要将连接归还到连接池中

细节分析

1.数据库连接的存储:可用使用STL中的队列queue

2.连接池连接的动态创建:这部分工作需要交给一个单独的线程来处理

3.连接池连接的动态销毁:这部分工作需要交给一个单独的线程来处理

4.数据库连接的添加和归还:这是一个典型的生产者和消费者模型

消费者:需要访问数据库的线程,数据库连接被取出(消费)

生产者:专门负责创建数据库连接的线程

处理生产者和消费者模型需要使用条件变量阻塞线程

5.连接池的默认连接数量:连接池中提供的可用连接的最小数量

如果不够就动态创建

如果太多就动态销毁

6.连接池的最大连接数量:能够创建的最大有效数据库连接上限

7.最大空闲时间:创建出的数据库连接在指定时间长度内一直未被使用,此时就需要销毁该连接。

8.连接超时:消费者线程无法获取到可用连接是,阻塞等待的时间长度

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

cpp 复制代码
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环境和代码下载参考这篇文章

6.1Qt中编译数据库驱动-CSDN博客https://blog.csdn.net/a16675900046/article/details/163799746?spm=1001.2014.3001.5501

VS配置MySQL环境

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

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

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

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

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

cpp 复制代码
// ============================================================================
// 头文件保护:防止同一个头文件被多次包含
// #pragma once 是预处理指令,作用等同于 #ifndef ... #endif
// ============================================================================
#pragma once

// ---- 包含队列头文件 ----
// 用于 std::queue,存储连接池中的空闲连接
// 队列是先进先出(FIFO)结构,保证连接被均匀使用
#include <queue>

// ---- 包含互斥锁头文件 ----
// 用于 std::mutex,保护队列在多线程环境下的线程安全
// 多个线程同时操作队列时,必须加锁
#include <mutex>

// ---- 包含 MySQL 连接类头文件 ----
// MysqlConn 是封装 MySQL C API 的连接类
// 连接池中存储的就是 MysqlConn 的指针
#include "MysqlConn.h"

// ---- 包含条件变量头文件 ----
// 用于 std::condition_variable,实现线程等待/通知机制
// 当队列为空时,线程在此等待;有新连接时,通知等待的线程
#include <condition_variable>

// ---- 包含输入输出流头文件 ----
// 用于 std::cout、std::endl 等,用于打印日志或错误信息
#include <iostream>

using namespace std; 

/*
* ============================================================================
* 数据库连接池类(单例模式)
*
* 功能:
*   1. 预先创建一批 MySQL 连接
*   2. 需要时从池中取出一个
*   3. 用完后归还到池中
*   4. 空闲连接超时自动释放
*   5. 连接不够时自动创建
*
* 设计模式:单例模式(整个程序只有一个连接池实例)
* ============================================================================
*/

class ConnectionPool
{
public:
    // ========================================================================
    // 公有方法(对外接口)
    // ========================================================================

    // ---- 获取单例对象 ----
    // 功能:获取连接池的唯一实例
    // 返回:ConnectionPool* 指向唯一的连接池对象
    // 设计模式:单例模式(Singleton Pattern)
    //
    // 实现方式:使用静态局部变量(C++11 起线程安全)
    //   static ConnectionPool pool;  // 第一次调用时初始化
    //   return &pool;
    //
    // 用法:
    //   ConnectionPool* pool = ConnectionPool::getConnectPool();
    static ConnectionPool* getConnectPool();

    // ---- 从连接池中取出一个连接 ----
    // 功能:从连接池中获取一个可用的 MySQL 连接
    // 返回:shared_ptr<MysqlConn> 智能指针,自动管理连接生命周期
    // 特点:当 shared_ptr 销毁时,连接会自动归还到池中
    //
    // 实现机制:使用 shared_ptr 的自定义删除器(deleter)
    //   return shared_ptr<MysqlConn>(conn, [this](MysqlConn* c) {
    //       // 归还连接到池中,而不是删除
    //       m_connectionQ.push(c);
    //   });
    //
    // 用法:
    //   auto conn = pool->getConnection();
    //   conn->query("SELECT ...");
    //   // conn 超出作用域时,连接自动归还
    shared_ptr<MysqlConn> getConnection();

    // ---- 删除拷贝构造函数 ----
    // 功能:禁止拷贝构造,确保单例唯一
    // 语法:= delete 表示删除该函数,调用即报错
    //
    // 为什么需要删除?
    //   单例模式要求全局只有一个实例,拷贝会破坏单例
    //   拷贝构造函数被删除后,以下代码会编译失败:
    //   ConnectionPool pool2 = *pool;  // ❌ 编译错误!
    ConnectionPool(const ConnectionPool& obj) = delete;

    // ---- 删除拷贝赋值运算符 ----
    // 功能:禁止拷贝赋值,确保单例唯一
    //
    // 为什么需要删除?
    //   单例模式要求全局只有一个实例,赋值会破坏单例
    //   拷贝赋值运算符被删除后,以下代码会编译失败:
    //   ConnectionPool pool2;
    //   pool2 = *pool;  // ❌ 编译错误!
    ConnectionPool& operator=(const ConnectionPool& obj) = delete;

private:
    // ========================================================================
    // 私有方法(内部使用,外部不可调用)
    // ========================================================================

    // ---- 构造函数私有化 ----
    // 功能:外部不能直接创建 ConnectionPool 对象
    // 只能通过 getConnectPool() 获取单例
    // 这是单例模式的核心:私有化构造函数
    //
    // 在构造函数中执行:
    //   1. 读取配置文件(parseJsonFile)
    //   2. 创建 minSize 个初始连接
    //   3. 启动生产者线程(produceConnection)
    //   4. 启动回收者线程(recycleConnection)
    ConnectionPool();

    // ---- 解析 JSON 配置文件 ----
    // 功能:从 JSON 文件读取连接池配置参数
    // 返回值:true 表示解析成功,false 表示解析失败
    //
    // 配置文件内容(config.json):
    // {
    //   "ip": "127.0.0.1",
    //   "port": 3306,
    //   "user": "root",
    //   "passwd": "123456",
    //   "dbName": "test_db",
    //   "minSize": 5,          // 最小连接数
    //   "maxSize": 20,         // 最大连接数
    //   "maxIdleTime": 60000,  // 最大空闲时间(毫秒)
    //   "timeout": 3000        // 获取连接超时(毫秒)
    // }
    bool parseJsonFile();

    // ---- 生产者线程:创建连接 ----
    // 功能:当连接池中的连接少于最小连接数时,主动创建新连接
    // 运行在独立的线程中(std::thread)
    //
    // 工作逻辑(伪代码):
    //   while (true) {
    //       {
    //           unique_lock<mutex> lock(m_mutexQ);
    //           if (m_connectionQ.size() < m_minSize) {
    //               addConnection();  // 补充连接
    //           }
    //       }
    //       this_thread::sleep_for(chrono::seconds(1));
    //   }
    void produceConnection();

    // ---- 回收者线程:释放空闲连接 ----
    // 功能:检查连接的空闲时间,超过 maxIdleTime 的连接被释放
    // 运行在独立的线程中(std::thread)
    //
    // 工作逻辑(伪代码):
    //   while (true) {
    //       unique_lock<mutex> lock(m_mutexQ);
    //       // 遍历队列,检查每个连接的空闲时间
    //       for (auto it = queue.begin(); it != queue.end(); ++it) {
    //           if (空闲时间 > m_maxIdleTime && 总连接数 > m_minSize) {
    //               delete *it;  // 释放连接
    //               it = erase(it);
    //           }
    //       }
    //       lock.unlock();
    //       this_thread::sleep_for(chrono::seconds(5));
    //   }
    void recycleConnection();

    // ---- 添加一个连接 ----
    // 功能:创建一个新的 MySQL 连接并放入连接池
    // 被 produceConnection() 调用
    //
    // 工作逻辑:
    //   1. MysqlConn* conn = new MysqlConn();
    //   2. conn->connect(m_ip, m_user, m_passwd, m_dbName, m_port);
    //   3. m_connectionQ.push(conn);  // 放入队列
    void addConnection();

    // ========================================================================
    // 配置参数(从 JSON 文件读取)
    // ========================================================================

    string m_ip;               // 数据库服务器 IP 地址(如 "127.0.0.1")
    string m_user;             // 数据库用户名(如 "root")
    string m_dbName;           // 数据库名称(如 "test_db")
    string m_passwd;           // 数据库密码
    unsigned short m_port;     // 数据库端口(MySQL 默认 3306)

    int m_minSize;             // 连接池最小连接数(始终保留)
                               // 例如:5,始终保持 5 个连接可用
                               // 连接少于这个数时,produceConnection 会补充

    int m_maxSize;             // 连接池最大连接数(防止过载)
                               // 例如:20,最多 20 个连接
                               // 达到这个数后,不再创建新连接

    int m_maxIdleTime;         // 连接最大空闲时间(毫秒)
                               // 例如:60000(1分钟)
                               // 连接空闲超过这个时间,会被回收释放

    int m_timeout;             // 获取连接的超时时间(毫秒)
                               // 例如:3000(3秒)
                               // 超过这个时间没有获取到连接,返回空指针

    // ========================================================================
    // 连接池核心数据结构
    // ========================================================================

    queue<MysqlConn*> m_connectionQ;  // 连接队列:存储所有空闲连接
                                      // FIFO(先进先出)保证连接均匀使用

    mutex m_mutexQ;                   // 互斥锁:保护队列的线程安全
                                      // 所有对 m_connectionQ 的操作都必须加锁

    condition_variable m_cond;        // 条件变量:等待可用连接
                                      // 当队列为空时,getConnection() 在这里等待
                                      // 当有连接归还时,notify_one() 唤醒等待的线程
};

添加一个json格式的配置文件

在当前的vs里配置环境

在部署的时候把编译出来的jsoncpp对应的库文件和头文件都拷贝到了c盘的jsoncpp目录下

相关推荐
TDengine (老段)1 小时前
TDengine 应用案例 — 工业大数据与智能制造
大数据·数据库·制造·时序数据库·tdengine·涛思数据
狂热开发者1 小时前
打印机卖到欧洲以后 App 更新了,旧包装二维码还能用吗
数据库
张3蜂1 小时前
2026 中国国产数据库 Top 20:市场格局、开源协议与竞品分析
数据库
烟雨归来1 小时前
Oracle 19c RAC登录风暴引发library cache lock生产故障分析
数据库·oracle
longxiaozhang62 小时前
C#异常处理:程序出错了怎么办?
开发语言·数据库·c#
NineData2 小时前
DTCC 2026 NineData 叶正盛:如何统一管理人与 AI Agent 的数据访问行为
数据库·人工智能·sql·oracle·agent·ninedata·dtcc
一入程序无退路2 小时前
Navicat导出Excel格式表名表结构
数据库·excel
记忆张量MemTensor2 小时前
MemOS Skill 上线|一句话即可接入 MemOS Cloud
大数据·数据库·人工智能·typescript·开源
朋友圈自动点赞工具2 小时前
NAS游戏库自动下载新方案,Questarr部署教程
服务器·数据库·游戏·科技资讯