数据库连接池概述
我们在进行数据库操作的时候为了提高数据库(关系型数据库)的访问瓶颈,除了在服务器端增加缓存服务器(例如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 的封装和使用
2.单例模式
3.STL容器
4.生产者和消费者模型
5.Jsoncpp库的使用(解析配置文件中的数据库相关信息)
连接池的设计
要设计一个数据库连接池,我们需要实现以下几个功能点:
1.连接池只需要一个实例,所以连接池类应该是一个单例模式的类
2.所有的数据库连接应该维护到一个安全的队列中
使用队列的目的是方便连接的添加和删除
所谓的安全指的是线程安全,也就是说需要使用互斥锁来保护队列数据的读写。
3.在需要的时候可以从连接池中得到一个或多个可用的数据库连接
如果有可用连接,直接取出
如果没有可用连接,阻塞等待一定时长然后再重试
4.如果队列中没有多余的可用连接,需要动态的创建新连接
5.如果队列中空闲的连接太多,需要动态的销毁一部分
6.数据库操作完毕,需要将连接归还到连接池中
细节分析
1.数据库连接的存储:可用使用STL中的队列queue
2.连接池连接的动态创建:这部分工作需要交给一个单独的线程来处理
3.连接池连接的动态销毁:这部分工作需要交给一个单独的线程来处理
4.数据库连接的添加和归还:这是一个典型的生产者和消费者模型
消费者:需要访问数据库的线程,数据库连接被取出(消费)
生产者:专门负责创建数据库连接的线程
处理生产者和消费者模型需要使用条件变量阻塞线程
5.连接池的默认连接数量:连接池中提供的可用连接的最小数量
如果不够就动态创建
如果太多就动态销毁
6.连接池的最大连接数量:能够创建的最大有效数据库连接上限
7.最大空闲时间:创建出的数据库连接在指定时间长度内一直未被使用,此时就需要销毁该连接。
8.连接超时:消费者线程无法获取到可用连接是,阻塞等待的时间长度
综上所述,数据库连接池对应的单例模式的类的设计如下:
cppusing 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环境和代码下载参考这篇文章
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目录下
基于C++11的数据库连接池
丰锋ff2026-08-26 14:51
相关推荐
TDengine (老段)1 小时前
TDengine 应用案例 — 工业大数据与智能制造狂热开发者1 小时前
打印机卖到欧洲以后 App 更新了,旧包装二维码还能用吗张3蜂1 小时前
2026 中国国产数据库 Top 20:市场格局、开源协议与竞品分析烟雨归来1 小时前
Oracle 19c RAC登录风暴引发library cache lock生产故障分析longxiaozhang62 小时前
C#异常处理:程序出错了怎么办?NineData2 小时前
DTCC 2026 NineData 叶正盛:如何统一管理人与 AI Agent 的数据访问行为一入程序无退路2 小时前
Navicat导出Excel格式表名表结构记忆张量MemTensor2 小时前
MemOS Skill 上线|一句话即可接入 MemOS Cloud朋友圈自动点赞工具2 小时前
NAS游戏库自动下载新方案,Questarr部署教程











