|
- 数据库连接池负责分配、管理、和释放数据库连接,允许使用应用程序重复使用一个现有的数据库连接。数据库连接是关键有限且昂贵的资源,一个数据库连接对象均对应一个物理数据库的连接,每次操作都打开一个物理连接,使用完都关闭连接.
- 基于C++实现Mysql数据库连接池实例
- 项目技术点
- C语言进行MYSQL数据库编程
- 无锁单例
- 基于STL队列加C++11新特性保证线程安全实现的生产者消费者模型
- C++11多线程编程 (线程间同步与互斥)
- 基于CAS的原子整形
- lambda表达式
- shared_ptr智能指针管理Connection*指针对象
- 基于C++11标准库实现, 具备跨平台的特性,省去了对于pthread库的C++的封装.更加针对于项目的核心逻辑上的思考和实现. (主干到细节)
- Makefile自动化编译
- 项目意义
- 高并发场景下, 频繁创建, 销毁连接带来的性能损耗
- 三次握手,连接认证(身份权限认证),MySQL资源释放, 四次挥手
- 每一次client 访问 Mysql server都需要进行上述操作. 上述这些操作是固定的流程. 真正的sql语句执行操作才是我们无法逃脱的, 所以上述这些我们完全可以提前创建出来,然后进行不断的复用connections. 实现mysql server访问的性能提升.
- 思考: 提前创建好的 connetions 数目是否是越多越好?
- 当然是不可能,因为每一个connection都是需要占据一定的系统资源的, 创建过多的connection 会导致服务器资源紧张. 起码per connection per socketfd (套接字资源)
- 思考: connections 数目设计?
- 上下限限制数目. initSize控制下限. maxSize控制连接上限 (一般是系统的最大mysql connetions num)
- 多余connection最长闲置时间限制: maxIdleTime (及时释放闲置连接)
- 资源紧张, 并发访问量高. 没有连接可用 :connectionTimeout (return error, 获取连接超时时间)
- 项目实现
- Connection设计
- 数据成员
- MYSQL* _conn; //连接句柄
- clock_t _startTime; //连接起始空闲时间
- 操作接口
- Connection();//构造 init Connection
- ~Connection(); //析构 destroy connection
- bool connect(ip, port, username, password, dbname); //连接操作, 返回连接结果
- bool update(sql);//表更新操作, 返回更新结果
- MYSQL_RES* query(sql); //查询操作, 返回查询结果
- void refreshStartTime(); //刷新连接起始空闲时间
- clock_t getAliveTime(); //获取连接空闲时间
- ConnectionPool设计
- 数据成员
- //连接登录信息
- string _ip;
- unsigned short _port;
- string _username;
- string _password;
- string _dbname;
- //连接数量等配置信息
- int _initSize;
- int _maxSize;
- int _maxIdleTime;
- int _connectionTimeout;
- //连接存储信息,以及保证线程安全
- queue<Connection*> _connectionQue;
- mutex _queueLock;
- condition_variable _cond;
- atomic_int _connectionCnt;
- 操作接口
- ConnectionPool(); //构造 init pool, 加载配置, 初始连接, 启动线程
- static ConnectionPool* getConnectionPool();//获取单例
- shared_ptr<Connection> getConnection();//获取连接
- int _loadConfigFile(string& filename); //加载解析配置信息
- void produceConnectionTask(); //生产连接任务
- void scannerConnetionTask(); //扫描监视销毁空闲线程任务
- 项目复杂接口细节刨析
- getConnection()
- /*
- 从连接池中获取一条连接. 相当于是消费者
- 消费前提, _connectionQue中有货, 所以无货期间需要进行阻塞休眠等待.
- 等待也不能一直等待. 存在连接超时时间,
- waittime >= _connectionTimeout then output << error.
- */
- shared_ptr<Connection> getConnection() {
- unique_lock<mutex> auto_lock(_queueLock);//定义智能锁
- while (_connectionQue.empty()) {
- if (cv_status::timeout == _cond.wait_for(auto_lock, chrono::milliseconds(_connectionTimeout))) { //达到连接超时时间
- if (_connectionQue.empty()) {
- //LOG DEBUG INFO
- cerr << "获取连接超时" << endl;
- return nullptr;
- }
- }
- }
- //定义shared_ptr<Connection> 自定义del函数, 而非直接调用~T()
- shared_ptr<Connection> sp(_connectionQue.front(), [&](Connection* pconn){
- unique_lock<mutex> auto_lock(_queueLock);
- pconn->refreshStartTime(); //刷新连接起始空闲时间
- _connectionQue.push(pconn);
- });
- _connectionQue.pop();
- _cond.notify_all(); //弹出任务, queue maybe empty notify produce
- return sp;
- }
- produceConnectionTask()
- /*
- 生产者线程任务. 在_connectionQue中无connetion
- && _connectionCnt < _maxSize 时候 及时创建连接填充_connectionQue。
- */
- void produceConnectionTask() {
- for (;;) {
- unique_lock<mutex> auto_lock(_queueLock);
- while (!_connectionQue.empty()) {
- _cond.wait(auto_lock);
- }
- if (_connectionCnt < _maxSize) {
- Connection* pconn = new Connection();
- pconn->connet(_ip, _port, _username, _password, _dbname);
- pconn->refreshStartTime();
- _connectionQue.push(pconn);
- _connectionCnt++;
- }
- _cond.notify_all(); //通知消费
- }
- }
-
- scannerConnetionTask()
- /*
- 不停的扫描所有的connection, 从头到尾的扫描,
- 监控销毁哪些长期空闲的connection, 避免对于空闲资源的无端占用浪费.
- 每一次休眠一个 _maxIdleTime 就出来 检查, 定期轮询检查空闲丽连接进行销毁
- */
- void scannerConnectionTask() {
- for (;;) {
- //休眠maxIdleTime
- this_thread::sleep_for(chrono::seconds(_maxIdleTime));
- unique_lock<mutex> auto_lock(_queueLock);
- while (_connectionCnt > _initSize)
- {
- Connection *p = _connectionQue.front();
- if (p->getAliveeTime()/SECONDS_PER_SEC >= _maxIdleTime)
- {
- _connectionQue.pop();
- _connectionCnt--;
- delete p; // 调用~Connection()释放连接
- }
- else
- {
- break; // 队头的连接没有超过_maxIdleTime,其它连接肯定没有
- }
- }
- }
- }
复制代码
|
|