基于C++实现Mysql数据库连接池实例

2022-12-10

数据库连接池负责分配、管理、和释放数据库连接,允许使用应用程序重复使用一个现有的数据库连接。数据库连接是关键有限且昂贵的资源,一个数据库连接对象均对应一个物理数据库的连接,每次操作都打开一个物理连接,使用完都关闭连接

目录
  • 项目技术点
  • 项目意义
  • 项目实现
    • Connection设计
    • ConnectionPool设计
  • 项目复杂接口细节刨析

    项目技术点

    • 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,其它连接肯定没有
    			}
    		}       
        }
    }

    到此这篇关于基于C++实现Mysql数据库连接池实例的文章就介绍到这了,更多相关C++数据库连接池内容请搜索北冥有鱼以前的文章或继续浏览下面的相关文章希望大家以后多多支持北冥有鱼!

    《基于C++实现Mysql数据库连接池实例.doc》

    下载本文的Word格式文档,以方便收藏与打印。