首頁 > 軟體

基於C++實現Mysql資料庫連線池範例

2022-12-09 14:04:11

專案技術點

  • 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++資料庫連線池內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com