高端网站制作哪家专业,开网店怎么开,长安区建设局官网站站,家居品牌网站设计论文前言
数据库连接池在开发中应该是很常用的一个组件#xff0c;他可以很好的节省连接数据库的时间开销#xff1b;本文基使用C实现了一个简单的数据库连接池#xff0c;代码量只有400行只有#xff0c;但是压力测试效果很好#xff1b;欢迎收藏 关注#xff0c;本人将会…前言
数据库连接池在开发中应该是很常用的一个组件他可以很好的节省连接数据库的时间开销本文基使用C实现了一个简单的数据库连接池代码量只有400行只有但是压力测试效果很好欢迎收藏 关注本人将会持续更新后端与AI算法有关知识点。 文章目录 连接池功能点介绍初始连接量最大连接量最大空闲时间连接超时时间 连接池主要包含了以下功能点代码压力测试 MySQL 数据库是基于 C/S 模式的在每一次访问mysql服务器的时候都需要建立一个TCP连接但是在高并发情况下大量的 TCP 三次握手、MySQL Server 连接认证、MySQL Server 关闭连接回收资源和 TCP 四次挥手所耗费的性能事件也是很明显的故设置连接池就是为了减少这一部分的性能损耗。
连接池功能点介绍
初始连接量
表示连接池事先会和 MySQL Serve r创建最小个数的连接当应用发起 MySQL 访问时不用再创建和 MySQL Server 新的连接直接从连接池中获取一个可用的连接即可使用完成后并不去释放连接而是把连接再归还到连接池当中。
最大连接量
当并发访问MySQL Server的请求增多时初始连接量已经不够用了此时会去创建更多的连接给应用去使用但是新创建的连接数量上限是maxSize不能无限制的创建连接。并且当这些连接使用完之后再次归还到连接池当中来维护。
最大空闲时间
当访问MySQL的并发请求多了以后连接池里面的连接数量会动态增加上限是maxSize 个当这些连接用完会再次归还到连接池当中。如果在指定的时间内这些新增的连接都没有被再次使用过那么新增加的这些连接资源就要被回收掉只需要保持初始连接量个连接即可。
连接超时时间
当MySQL的并发请求量过大连接池中的连接数量已经到达最大数量了而此时没有空闲的连接可供使用那么此时应用从连接池获取连接无法成功它通过阻塞的方式获取连接的时间如果超过一个时间那么获取连接失败。
连接池主要包含了以下功能点
单例模式设置连接池向用户提供一个接口可以从池中拿到一个数据库连接采用生产者-消费者模式池用队列作为缓冲区实现生成连接和拿取连接采用锁在创建、拿取连接中进行加锁采用智能指针管理从队列中获取的连接并且采用lambda实现智能指针的析构函数将连接从新放回队列中设置生产连接、回收连接线程并且驻留后台作为守护线程采用原子变量才记录当前池中的连接数。
创建目录如下 代码
logger.h
#ifndef PUBLIC_H_
#define PUBLIC_H_#include iostream// 作用封装简单的LOG
#define LOGGER(str) std::cout 》 __LINE__ time: __TIME__ message: str std::endl;#endif // !PUBLIC_H_connection.h
#ifndef CONNECTION_H_
#define CONNECTION_H_#include mysql/mysql.h
#include ctime
#include string/*
功能初始化数据库连接释放连接连接数据库查询mysql修改数据库数据刷新/设置空闲时间的起始时间点返回空闲时间
*/class Connection
{
public:// 初始化数据库连接Connection();// 释放连接~Connection();// 连接数据库bool connectionSqlBase(std::string ip, unsigned int port, std::string user, std::string passward, std::string dbName);// 查询mysqlMYSQL_RES* query(std::string sql);// 修改数据库数据bool modify(std::string sql);// 刷新/设置空闲时间的起始时间点void setStartActivateTime();// 返回空闲时间clock_t getActivateTime();
private: MYSQL* m_sqlConn{}; // 连接mysql服务器clock_t m_activateTime; // 记录空闲时间的起始点
};#endif // !CONNECTION_H_connection.cpp
#include connection.h
#include logger.h// 初始化数据库连接
Connection::Connection()
{m_sqlConn mysql_init(nullptr);if(m_sqlConn nullptr) {LOGGER(mysql init false !!!);return;}
}
// 释放连接
Connection::~Connection()
{mysql_close(m_sqlConn);
}
// 连接数据库
bool Connection::connectionSqlBase(std::string ip, unsigned int port, std::string user, std::string passward, std::string dbName)
{if(nullptr mysql_real_connect(m_sqlConn, ip.c_str(), user.c_str(), passward.c_str(), dbName.c_str(), port, NULL, 0)) {LOGGER(mysql connects error!!);return false;}return true;
}
// 查询mysql
MYSQL_RES* Connection::query(std::string sql)
{int res mysql_query(m_sqlConn, sql.c_str());if(0 ! res) {LOGGER(sql query false!!!);return nullptr;}return mysql_use_result(m_sqlConn);
}
// 修改数据库数据
bool Connection::modify(std::string sql)
{int res mysql_query(m_sqlConn, sql.c_str());if(0 ! res) {LOGGER(sql update/insert/select false!!!);return false;}return true;
}
// 刷新/设置空闲时间的起始时间点
void Connection::setStartActivateTime()
{m_activateTime clock();
}
// 返回空闲时间
clock_t Connection::getActivateTime()
{return clock() - m_activateTime;
}dbConnectionPool.h
#ifndef DBCONNECTION_H_
#define DBCONNECTION_H_#include connection.h
#include mysql/mysql.h
#include queue
#include string
#include condition_variable
#include atomic
#include memory// 核心生产者、消费者模式class DbConnPool
{
public: // 单例模式static DbConnPool* getDbConnPool();// 对外提供接口: 获取连接的数据库通过智能指针回收std::shared_ptrConnection getMysqlConn();// 测试// void test()// {// readConfigurationFile();// }private: // 单例模型构造函数私有化, 目的创建最小连接数量DbConnPool();DbConnPool(const DbConnPool) delete;DbConnPool operator(const DbConnPool) delete;// 读取配置文件bool readConfigurationFile();// 如果没有Mysql连接了则产生新连接这个线程驻留后台(像守护线程一样)void produceNewConn();// 如果队列线程 initSize 且空闲时间大于最大空闲时间则回收void recycleConn();private:// MYSQL连接信息std::string m_ip;unsigned int m_port;std::string m_username;std::string m_password;std::string m_dbname;// 数据库连接池信息int m_initSize;int m_maxSize;int m_maxFreeTime;int m_maxConnTime;// 生产者、消费者共享内存获取连接std::queueConnection* m_connQueue;// 存储当前存储到队列中存储的数量std::atomic_int m_conntionCnt{};// 锁std::mutex m_queueMuetx;// 生产者、消费者产生连接和取连接std::condition_variable m_cv; // 用于生产线程和消费线程之间的通信
};#endif // !DBCONNECTION_H_dbConnectionPool.cpp
#include dbConnectionPool.h
#include connection.h
#include logger.h#include iostream
#include cstdio
#include cstring
#include mutex
#include thread
#include functional
#include chrono// 创建连接new但是拿取连接后是交给shared_ptr// 单例模式
DbConnPool* DbConnPool::getDbConnPool()
{// 最简单的方式staticstatic DbConnPool pool;return pool;
}// 对外提供接口: 获取连接的数据库通过智能指针回收
std::shared_ptrConnection DbConnPool::getMysqlConn()
{std::unique_lockstd::mutex lock(m_queueMuetx);// 判断队列是否为空while(m_connQueue.empty()) {// 队列为空则等待 最大连接时间, 即这个时候客户端请求连接但是池里没有连接了则会等待如果超过了最大时间则连接失败if(std::cv_status::timeout m_cv.wait_for(lock, std::chrono::seconds(m_maxConnTime))) {// 再次判断是否为空if(m_connQueue.empty()) {LOGGER(no conntion !!!!);return nullptr;}}}/*从队列中获取一个连接交给**智能指针管理**注意删除连接有回收线程监控而这里获取的连接使用完后需要还给**队列中**所以**需要重写智能指针的回收函数***/std::shared_ptrConnection sp(m_connQueue.front(), [](Connection* pconn){// 注意这里需要加锁std::unique_lockstd::mutex lock(m_queueMuetx);pconn-setStartActivateTime();m_connQueue.push(pconn); // 入队m_conntionCnt; // 1});// 弹出队列m_connQueue.pop();m_conntionCnt--; // -1return sp;
}// 单例模型构造函数私有化
DbConnPool::DbConnPool()
{if(readConfigurationFile() false) {return;}std::unique_lockstd::mutex lock(m_queueMuetx);for(int i 0; i m_maxSize; i) {Connection* newConn new Connection();newConn-connectionSqlBase(m_ip, m_port, m_username, m_password, m_dbname);newConn-setStartActivateTime(); // 设置 空闲时间 的起始点m_connQueue.push(newConn); // 入队m_conntionCnt; // 存储到队列中数据1}// 开启线程检查是否需要需要**新创建连接**std::thread produce(std::bind(DbConnPool::produceNewConn, this));produce.detach(); // 驻留后台// 开启线程检查是否需要**删除连接**std::thread search(std::bind(DbConnPool::recycleConn, this));search.detach(); // 驻留后台
}// 读取配置文件
bool DbConnPool::readConfigurationFile()
{FILE* fp fopen(./mysql.ini, r);if(fp nullptr) {LOGGER(mysql.ini open false!!);return false;}char buff[BUFSIZ] { 0 };while(!feof(fp)) {// clearmemset(buff, 0, sizeof(buff));// 读取fgets(buff, BUFSIZ, fp);std::string str buff;// 判空if(str.empty()) {continue;}// 截断int idx str.find(, 0);if(idx -1) {continue;}int end str.find(\n, idx);std::string key str.substr(0, idx);std::string value str.substr(idx 1, end - idx - 1);//std::cout key: key value: value std::endl;if(key ip) {m_ip value;} else if(key port) {m_port atoi(value.c_str());} else if(key username) {m_username value;} else if(key password) {m_password value;} else if(key dbname) {m_dbname value;} else if(key initSize) {m_initSize atoi(value.c_str());} else if(key maxSize) {m_maxSize atoi(value.c_str());} else if(key maxFreeTime) {m_maxFreeTime atoi(value.c_str());} else if(key maxConnTime) {m_maxConnTime atoi(value.c_str());}}std::cout m_ip m_port m_username m_password m_dbname m_initSize m_maxSize m_maxFreeTime m_maxConnTime std::endl;return true;
}// 如果池里没有Mysql连接了则产生新连接这个线程驻留后台(像守护线程一样)
/*
实现思路设置一个循环循环检查如果队列不为空则条件变量一直等待
*/
void DbConnPool::produceNewConn()
{for(;;) {std::unique_lockstd::mutex lock(m_queueMuetx);while(!m_connQueue.empty()) {m_cv.wait(lock); // 条件变量一直等待}// 这个时候队列为空从新创建连接for(int i 0; i m_maxSize; i) {Connection* newConn new Connection();newConn-setStartActivateTime(); // 刷新时间m_connQueue.push(newConn);m_conntionCnt; // 1}// 通知等待线程m_cv.notify_all();}
}// 如果队列线程 initSize 且空闲时间大于最大空闲时间则回收
void DbConnPool::recycleConn()
{for(;;) {std::unique_lockstd::mutex lock(m_queueMuetx);while(m_conntionCnt m_initSize) {Connection* conn m_connQueue.front();// 超过最大空闲时间if((static_castdouble(conn-getActivateTime()) / CLOCKS_PER_SEC) m_maxFreeTime) {m_connQueue.pop();m_conntionCnt--;delete conn;} else { // 对头没超过则直接退出break;}}}
}压力测试
分别插入10000条数据对没有使用连接池和使用连接池分别进行测试。
#include dbConnectionPool.h
#include connection.h
#include iostream
#include mysql/mysql.h
#include chronoint main() {auto start std::chrono::high_resolution_clock::now(); // 获取当前时间点for(int i 0; i 10000; i) {
#if 1DbConnPool* pool DbConnPool::getDbConnPool();std::shared_ptrConnection conn pool-getMysqlConn();char sql[1024] { 0 };sprintf(sql, insert into temp(name, age, sex) values(%s, %d, %s);, yxz, 18, man);conn-modify(sql);
#elif 0Connection conn;conn.connectionSqlBase(127.0.0.1, 3306, root, wy2892586, test);char sql[1024] { 0 };sprintf(sql, insert into temp(name, age, sex) values(%s, %d, %s);, yxz, 18, man);conn.modify(sql);
#endif}auto end std::chrono::high_resolution_clock::now(); // 获取结束时间点std::chrono::durationdouble, std::milli duration end - start; // 计算持续时间并转换为毫秒std::cout Time: duration.count() ms std::endl;return 0;
}结果如下