介绍
为提高mysql的访问性能,可增加连接池。为什么他能提高性能: mysql是基于C/S架构,从客户端到服务器,一条sql的执行流程:tcp三次握手->mysql连接认证->执行sql->关闭mysql连接->tcp四次挥手 每次数据库都需要这5步太耗时,连接池缓存连接,后续直接用,5步变1步。

(图片来源网络,侵删)
连接池原理
一般设置成单例,用一个队列存放所有的空闲连接。
组成部分:

(图片来源网络,侵删)
1. 认证所需的信息 2. 初始连接数:创建单例时,构造函数就会创建初始连接数量的连接,以供使用 3. 最大连接数:存在的连接数不能超过它 4. 连接超时时间:当获取一条连接花费的时间超过此时间,则返回(如连接数达到最大了,又没有人释放) 5. 最大空闲时间:当连接在队列里存活的时间超过了此时间,且连接数>初始连接数,就删除多余连接直至初始连接数
代码示例,完整代码见https://github.com/1412771048/connect_pool:
#pragma once #include #include #include #include #include #include #include #include #include "mysql.hpp" #include "SimpleIni.h" //连接池类 class ConnectPool { public: ConnectPool(const ConnectPool&) = delete; ConnectPool(ConnectPool&&) = delete; ConnectPool& operator=(const ConnectPool&) = delete; ConnectPool& operator=(ConnectPool&&) = delete; ~ConnectPool() = default; static ConnectPool& GetInstance(); //给外部提供的接口:从连接池获取一条连接,且用智能指针管理,自定义删除器,使其析构时归还连接而不是释放连接 std::unique_ptr GetConnection(); private: ConnectPool(); // 构造函数私有化,单例 bool LoadConfig(); // 加载配置文件 std::string ip_; // mysql的ip uint16_t port_; // mysql的port std::string username_; // 登录mysql的用户名 std::string password_; // 登录mysql的密码 std::string database_; // 要访问的数据库名 uint32_t initSize_; // 初始连接数 uint32_t maxSize_; // 最大连接数 uint16_t maxIdleTime_; // 最大空闲时间 s int connectTimeout_; // 获取连接的超时时间 ms std::queue connQue_; // 存储空闲连接的队列 std::mutex queueMtx_; // 保护队列的互斥锁 std::condition_variable cv_; //条件变量 std::atomic connectCnt_; //记录连接的总数,且是线程安全的 }; ConnectPool& ConnectPool::GetInstance() { static ConnectPool pool; //静态局部变量的初始化是线程安全的 return pool; } bool ConnectPool::LoadConfig() { CSimpleIniA ini; if (ini.LoadFile("mysql.conf") connect(ip_, port_, username_, password_, database_)) { break; } } //出循环就一定连接上了 connQue_.push(p); p->refreshAliveTime(); ++connectCnt_; } //创建一个生产者线程,等待连接不够请求再创建的请求 std::thread produce([&](){ while (1) { std::unique_lock lock(queueMtx_); while (!connQue_.empty()) {//说明初始连接数都没用完 cv_.wait(lock); } //被唤醒说明初始连接数用完且不够了,拿到锁开始生产 if (connectCnt_ connect(ip_, port_, username_, password_, database_)) { break; } } //出循环就一定连接上了 connQue_.push(p); p->refreshAliveTime(); ++connectCnt_; } //通知等待的消费者们 cv_.notify_all(); } });//函数较短就原地写,长就拆走,也可用std::bind produce.detach(); //开一个线程专门扫描超过最大空闲时间,进行连接回收 std::thread scan([&](){ while (1) { //用sleep模拟定时,每次睡一个最大空闲时间 std::this_thread::sleep_for(std::chrono::seconds(maxIdleTime_)); //可能会操作队列,要加锁 std::unique_lock lock(queueMtx_); while (connectCnt_ > initSize_) { //队首元素的存活时间最长,若它都没超过最大空闲时间,则可以不用判断了 MySql* p = connQue_.front(); if (p->GetAliveTime() 归还连接 std::unique_ptr sp(connQue_.front(), [&](MySql* p){ std::unique_lock lock(queueMtx_); connQue_.push(p); p->refreshAliveTime(); }); connQue_.pop(); //消费后若队列空,则通知生产者 if (connQue_.empty()) { cv_.notify_all(); } return sp; }