如何利用C++實(shí)現(xiàn)mysql數(shù)據(jù)庫(kù)的連接池詳解
為什么是mysql?
現(xiàn)在幾乎所有的后臺(tái)應(yīng)用都要用到數(shù)據(jù)庫(kù),什么關(guān)系型的、非關(guān)系型的;正當(dāng)關(guān)系的,不正當(dāng)關(guān)系的;主流的和非主流的, 大到Oracle,小到sqlite,以及包括現(xiàn)在逐漸流行的基于物聯(lián)網(wǎng)的時(shí)序數(shù)據(jù)庫(kù),比如濤思的TDengine,咱們中國(guó)人自己的開(kāi)源時(shí)序數(shù)據(jù)庫(kù),性能杠杠滴。
凡此總總,即使沒(méi)用過(guò),也聽(tīng)說(shuō)過(guò),但大部分人或企業(yè)用的最多的就是白嫖型數(shù)據(jù)庫(kù):mysql。該數(shù)據(jù)庫(kù)的特點(diǎn)就是無(wú)論是個(gè)人還是企業(yè)都能玩的起。像Oracle這種名媛型數(shù)據(jù)庫(kù)基本就屬于銀行特供,銀行需要花錢(qián)買(mǎi)平安,心里踏實(shí)。不買(mǎi)對(duì)的,只選貴的,因?yàn)槿思掖_實(shí)不差錢(qián)。
如果你的后臺(tái)應(yīng)用連數(shù)據(jù)庫(kù)都不需要,那跟咸魚(yú)網(wǎng)站有什么區(qū)別呢?就是咸魚(yú)二手網(wǎng)也要用到數(shù)據(jù)庫(kù)的。如果一個(gè)IT民工一輩子沒(méi)用過(guò)數(shù)據(jù)庫(kù)就在35(~45)歲時(shí)“被退休”,那他的職業(yè)生涯是遺憾的,是不完美的,是不純粹的。 好歹年輕是也要用一下非主流的Access吧,哪怕Execel也成。這種感覺(jué)就好比在大學(xué)時(shí)沒(méi)談過(guò)戀愛(ài)一樣,光忙著羨慕別人就突然畢業(yè)了。
為什么要搞資源池?
目前大部分后臺(tái)程序都選擇Java開(kāi)發(fā)或PHP,這兩種語(yǔ)言的第三方庫(kù)非常豐富,豐富到讓開(kāi)發(fā)人員的只要將精力放在具體業(yè)務(wù)上即可。比如數(shù)據(jù)庫(kù)的資源池,只要選擇好適當(dāng)?shù)膉ar包外加配置好相應(yīng)的數(shù)據(jù)庫(kù)參數(shù),即可放心大膽的使用mysql。
當(dāng)然,如果你命硬的話(huà),也可以選擇用C或C++開(kāi)發(fā)后臺(tái)應(yīng)用。這時(shí)候你就需要自己DIY一個(gè)數(shù)據(jù)庫(kù)資源池。
如果只是一個(gè)客戶(hù)端程序,基本不需要連接池,但對(duì)于后臺(tái)應(yīng)用來(lái)說(shuō),高并發(fā)就意味著多線(xiàn)程,多線(xiàn)程程就意味著資源的競(jìng)爭(zhēng)。內(nèi)存訪問(wèn)如此,數(shù)據(jù)庫(kù)訪問(wèn)也是如此。每次數(shù)據(jù)庫(kù)的打開(kāi)和關(guān)閉就是一次網(wǎng)絡(luò)連接和關(guān)閉的過(guò)程,頻繁的打開(kāi)和關(guān)閉無(wú)疑會(huì)浪費(fèi)大量的系統(tǒng)資源。這時(shí)候就需要提前建立好N個(gè)連接,并放在資源池中并提供給不同線(xiàn)程訪問(wèn)使用。
mysql資源池實(shí)現(xiàn)的案例源碼
我一直相信好的代碼是不需要過(guò)的語(yǔ)言來(lái)解釋的,代碼即文檔,要啥自行車(chē)。以下案例只是一個(gè)實(shí)現(xiàn)思路,供參考。
頭文件:MysqlPool.h
#pragma warning(disable : 4786) #include <windows.h> #include <winsock2.h> #include <mysql.h> // 確保你的機(jī)器有mysql開(kāi)發(fā)庫(kù) #include <vector> #include <string> using namespace std; #define DEFAULT_POOL_SIZE 20 // 缺省mysql連接池中的數(shù)量 #define DEFAULT_POOL_TIMEOUT 60 // 獲取池中mysql連接的超時(shí) // 自定義數(shù)據(jù)庫(kù)查詢(xún)回調(diào)函數(shù) typedef BOOL (CALLBACK *LPFN_RetrieveRecordData)(MYSQL_ROW& sqlRow, MYSQL_FIELD* pSqlFields, int iFieldCount, DWORD dwUserData); // Mysql數(shù)據(jù)庫(kù)連接類(lèi) class CMysqlConn { public: CMysqlConn(const char* pszDBServer, UINT uDBPort, const char* pszDBName, const char* pszDBUser, const char* pszDBPwd); virtual ~CMysqlConn(); public: // 打開(kāi)/關(guān)閉一個(gè)mysql連接 BOOL Open(); void Close(); // ping連接是否已關(guān)閉 BOOL Ping(); // 重置字符集 BOOL ResetCharset(); public: // ================SQL語(yǔ)句操作(簡(jiǎn)單實(shí)現(xiàn)幾個(gè))================ // 查詢(xún) BOOL Select(const char* pszSql, LPFN_RetrieveRecordData lpfnRetrieveRecordData, DWORD dwUserData); // 執(zhí)行 BOOL Execute(const char* pszSql); // 插入,如果主鍵是自增整型,返回插入后的主鍵值 __int64 Insert(const char* pszSql); protected: MYSQL* m_pMysql; // mysql數(shù)據(jù)庫(kù)操作對(duì)象 // 以下是連接mysql需要的參數(shù) string m_strDBServer; // mysql數(shù)據(jù)庫(kù)所在服務(wù)器 UINT m_uDBPort; // mysql數(shù)據(jù)庫(kù)連接端口 string m_strDBName; // 數(shù)據(jù)庫(kù)名稱(chēng) string m_strDBUser; // 數(shù)據(jù)庫(kù)賬戶(hù) string m_strDBPwd; // 數(shù)據(jù)庫(kù)密碼 }; // 數(shù)據(jù)庫(kù)連接池實(shí)現(xiàn) class CMysqlPool { public: CMysqlPool(); virtual ~CMysqlPool(); // 創(chuàng)建mysql連接池 BOOL Create(const char* pszDBServer, UINT uDBPort, const char* pszDBName, const char* pszDBUser, const char* pszDBPwd, DWORD dwPoolSize = DEFAULT_POOL_SIZE, DWORD dwTimeOut = DEFAULT_POOL_TIMEOUT); // 銷(xiāo)毀連接池 void Destroy(); public: // 獲取一個(gè)mysql連接 CMysqlConn* Get(); // 釋放一個(gè)mysql連接 void Release(CMysqlConn* pConn); protected: HANDLE m_hSemaphore; // 信號(hào)量句柄 DWORD m_dwPoolSize; // 連接池大小 DWORD m_dwTimeOut; // 超時(shí),單位秒 CRITICAL_SECTION m_csPool; // 連接池鎖 vector<CMysqlConn*> m_vecIdle; // 閑隊(duì)列 vector<CMysqlConn*> m_vecBusy; // 忙隊(duì)列 };
實(shí)現(xiàn)文件:MysqlPool.cpp
#include "stdafx.h" #include "MysqlPool.h" #include <assert.h> #include <algorithm> #pragma comment(lib, "libmysql.lib") //連接MysQL需要的庫(kù) ////////////////////////////////////////////////////////////////////// // CMysqlConn: mysql數(shù)據(jù)庫(kù)連接類(lèi) ////////////////////////////////////////////////////////////////////// CMysqlConn::CMysqlConn(const char* pszDBServer, UINT uDBPort, const char* pszDBName, const char* pszDBUser, const char* pszDBPwd) { assert(pszDBServer); assert(pszDBName); assert(pszDBUser); assert(pszDBPwd); m_pMysql = NULL; m_strDBServer = pszDBServer; m_uDBPort = uDBPort; m_strDBName = pszDBName; m_strDBUser = pszDBUser; m_strDBPwd = pszDBPwd; } CMysqlConn::~CMysqlConn() { Close(); } // 打開(kāi)一個(gè)mysql數(shù)據(jù)庫(kù),即建立一個(gè)數(shù)據(jù)庫(kù)連接 BOOL CMysqlConn::Open() { if(m_pMysql) { mysql_close(m_pMysql); // 關(guān)閉連接 m_pMysql = NULL; } m_pMysql = mysql_init(NULL); if(!m_pMysql) return FALSE; // 連接數(shù)據(jù)庫(kù) if(!mysql_real_connect(m_pMysql, m_strDBServer.c_str(), m_strDBUser.c_str(), m_strDBPwd.c_str(), m_strDBName.c_str(), m_uDBPort, NULL, 0)) { int i = mysql_errno(m_pMysql); const char * pszErr = mysql_error(m_pMysql); return FALSE; } // 設(shè)置重連 char chValue = 1; mysql_options(m_pMysql, MYSQL_OPT_RECONNECT, &chValue); mysql_query(m_pMysql,"set names 'gbk'"); return TRUE; } // 關(guān)閉數(shù)據(jù)庫(kù)連接 void CMysqlConn::Close() { if(m_pMysql) mysql_close(m_pMysql); // 斷開(kāi)連接 m_pMysql = NULL; } // ping一下mysql,看看連接還活著 BOOL CMysqlConn::Ping() { if(m_pMysql) return (0 == mysql_ping(m_pMysql)); return FALSE; } // 設(shè)置字符集為GBK BOOL CMysqlConn::ResetCharset() { if(m_pMysql) return (0 == mysql_query(m_pMysql, "set names 'gbk'")); return FALSE; } // mysql執(zhí)行:delete 或 update BOOL CMysqlConn::Execute(const char* pszSql) { assert(pszSql); if(!m_pMysql) return FALSE; MYSQL_STMT *myStmt = mysql_stmt_init(m_pMysql); if(!myStmt) { return FALSE; } if(0 != mysql_stmt_prepare(myStmt, pszSql, strlen(pszSql))) { mysql_stmt_close(myStmt); return FALSE; } if(0 != mysql_stmt_execute(myStmt)) { mysql_stmt_close(myStmt); return FALSE; } mysql_stmt_close(myStmt); return TRUE; } // mysql插入 __int64 CMysqlConn::Insert(const char* pszSql) { assert(pszSql); MYSQL_STMT *myStmt = mysql_stmt_init(m_pMysql); if(!myStmt) return 0; if(0 != mysql_stmt_prepare(myStmt, pszSql, strlen(pszSql))) { int i = mysql_errno(m_pMysql); const char * s = mysql_error(m_pMysql); mysql_stmt_close(myStmt); return 0; } if(0 != mysql_stmt_execute(myStmt)) { mysql_stmt_close(myStmt); return 0; } mysql_stmt_close(myStmt); __int64 i64ID = mysql_insert_id(m_pMysql); return i64ID; } // mysql查詢(xún) BOOL CMysqlConn::Select(const char* pszSql, LPFN_RetrieveRecordData lpfnRetrieveRecordData, DWORD dwUserData) { if(!m_pMysql) return FALSE; if(NULL == lpfnRetrieveRecordData) return FALSE; if(0 != mysql_real_query(m_pMysql, pszSql, strlen(pszSql))) { return FALSE; } MYSQL_RES *resRecord = mysql_store_result(m_pMysql); int iFieldCount = resRecord->field_count; MYSQL_ROW sqlRow; while (sqlRow = mysql_fetch_row(resRecord)) { if(!lpfnRetrieveRecordData(sqlRow, resRecord->fields, iFieldCount, dwUserData)) break; } mysql_free_result(resRecord); return TRUE; } ////////////////////////////////////////////////////////////////////// // CMysqlPool: mysql數(shù)據(jù)庫(kù)連接池類(lèi) ////////////////////////////////////////////////////////////////////// CMysqlPool::CMysqlPool() { ::InitializeCriticalSection(&m_csPool); } CMysqlPool::~CMysqlPool() { Destroy(); ::DeleteCriticalSection(&m_csPool); } // 創(chuàng)建mysql連接池 BOOL CMysqlPool::Create(const char* pszDBServer, UINT uDBPort, const char* pszDBName, const char* pszDBUser, const char* pszDBPwd, DWORD dwPoolSize, DWORD dwTimeOut) { m_dwTimeOut = dwTimeOut; m_dwPoolSize = dwPoolSize; // 創(chuàng)建信號(hào)量 m_hSemaphore = ::CreateSemaphore(NULL, dwPoolSize, dwPoolSize, NULL); if (NULL == m_hSemaphore) { return FALSE; } // 創(chuàng)建數(shù)據(jù)庫(kù)連接池 for(DWORD i = 0; i < dwPoolSize; ++i) { // 創(chuàng)建一個(gè)mysql數(shù)據(jù)庫(kù)連接 CMysqlConn *pConn = new CMysqlConn(pszDBServer, uDBPort, pszDBName, pszDBUser, pszDBPwd); if(!pConn->Open()) { delete pConn; continue; } m_vecIdle.push_back(pConn); } return m_vecIdle.size() > 0; } // 銷(xiāo)毀mysql連接池 void CMysqlPool::Destroy() { ::CloseHandle(m_hSemaphore); m_hSemaphore = NULL; // 釋放idle隊(duì)列 vector<CMysqlConn*>::iterator it; for(it = m_vecIdle.begin(); it != m_vecIdle.end(); ++it) { CMysqlConn* pConn = *it; delete pConn; } m_vecIdle.clear(); // 釋放busy隊(duì)列 while(!m_vecBusy.empty()) { CMysqlConn* pConn = m_vecBusy.back(); m_vecBusy.pop_back(); delete pConn; } } // 從mysql連接池獲取一個(gè)連接 CMysqlConn* CMysqlPool::Get() { DWORD dwRet = ::WaitForSingleObject(m_hSemaphore, m_dwTimeOut*1000); if (WAIT_OBJECT_0 != dwRet) // 超時(shí),說(shuō)明資源池沒(méi)有可用mysql連接 { printf("數(shù)據(jù)庫(kù)沒(méi)有可用連接。\r\n"); return NULL; } // 從連接池中獲取一個(gè)閑置連接 CMysqlConn* pConn = NULL; ::EnterCriticalSection(&m_csPool); if (!m_vecIdle.empty()) { pConn = m_vecIdle.back(); // 移出idle隊(duì)列 m_vecIdle.pop_back(); m_vecBusy.push_back(pConn); // 加入busy隊(duì)列 } ::LeaveCriticalSection(&m_csPool); if(NULL == pConn) return NULL; // 如果一個(gè)連接長(zhǎng)時(shí)間無(wú)通信,可能被防火墻關(guān)閉,此時(shí)可以通過(guò)mysql_ping函數(shù)測(cè)試一下 // 本例中通過(guò)重新設(shè)置字符集 // 重新設(shè)置字符集,并判斷數(shù)據(jù)庫(kù)連接是否已斷開(kāi) if(!pConn->ResetCharset()) { if(!pConn->Open()) return NULL; } printf("==》資源池:記得還我哦。\r\n"); return pConn; } // 釋放一個(gè)連接到mysql連接池 void CMysqlPool::Release(CMysqlConn* pConn) { if(NULL == pConn) return; // 釋放一個(gè)信號(hào)量 ::ReleaseSemaphore(m_hSemaphore, 1, NULL); ::EnterCriticalSection(&m_csPool); // 從Busy隊(duì)列中釋放該連接 vector<CMysqlConn*>::iterator it = find(m_vecBusy.begin(), m_vecBusy.end(), pConn); if(it != m_vecBusy.end()) { printf("POOL SIZE : %d, %d\r\n", m_vecIdle.size(), m_vecBusy.size()); m_vecBusy.erase(it); // 移出busy隊(duì)列 m_vecIdle.push_back(pConn); // 加入idle隊(duì)列 printf("POOL SIZE : %d, %d\r\n", m_vecIdle.size(), m_vecBusy.size()); } ::LeaveCriticalSection(&m_csPool); printf("《==資源池說(shuō):有借有還再借不難,常來(lái)玩啊。\r\n"); }
測(cè)試函數(shù)
void TestMysqlPool() { // 創(chuàng)建mysql連接資源池 CMysqlPool mysqlPool; if(!mysqlPool.Create("127.0.0.1", 3306, "information_schema", "root", "123456")) { printf("Create mysql conneticon pool failed.\r\n"); return; } // 從資源池中獲取一個(gè)連接,連接池說(shuō):記得要還哦! CMysqlConn* pConn = mysqlPool.Get(); // 假裝做一次數(shù)據(jù)庫(kù)操作 char* pszSQL = "SELECT * FROM CHARACTER_SETS"; pConn->Select(pszSQL, RetrieveRecordData, 0); // 將連接還給資源池并謝謝!連接池說(shuō):不客氣! mysqlPool.Release(pConn); printf("Test over.\r\n"); }
輸出打印
總結(jié)
到此這篇關(guān)于如何利用C++實(shí)現(xiàn)mysql數(shù)據(jù)庫(kù)連接池的文章就介紹到這了,更多相關(guān)C++實(shí)現(xiàn)mysql連接池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- c++連接mysql數(shù)據(jù)庫(kù)的兩種方法(ADO連接和mysql api連接)
- C/C++ 連接MySql數(shù)據(jù)庫(kù)的方法
- C++連接并使用MySQL數(shù)據(jù)庫(kù)
- C++用mysql自帶的頭文件連接數(shù)據(jù)庫(kù)
- C++連接mysql數(shù)據(jù)庫(kù)的兩種方法小結(jié)
- C++連接mysql數(shù)據(jù)庫(kù)并讀取數(shù)據(jù)的具體步驟
- 基于C++實(shí)現(xiàn)Mysql數(shù)據(jù)庫(kù)連接池實(shí)例
- C/C++代碼操作MySQL數(shù)據(jù)庫(kù)詳細(xì)步驟
相關(guān)文章
C語(yǔ)言 深入解讀數(shù)據(jù)結(jié)構(gòu)之堆的實(shí)現(xiàn)
堆就是用數(shù)組實(shí)現(xiàn)的二叉樹(shù),所以它沒(méi)有使用父指針或者子指針。堆根據(jù)“堆屬性”來(lái)排序,“堆屬性”決定了樹(shù)中節(jié)點(diǎn)的位置2021-11-11詳解C語(yǔ)言中的動(dòng)態(tài)內(nèi)存管理
對(duì)于數(shù)據(jù)的存儲(chǔ)我們可以靜態(tài)存儲(chǔ),也可以動(dòng)態(tài)存儲(chǔ),兩種方式都有自己特有的好處,這篇文章教我們?nèi)绾瓦M(jìn)行動(dòng)態(tài)的數(shù)據(jù)存儲(chǔ)?。。?!感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-12-12C++二叉樹(shù)實(shí)現(xiàn)詞頻分析功能
這篇文章主要為大家詳細(xì)介紹了C++二叉樹(shù)實(shí)現(xiàn)詞頻分析功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12使用設(shè)計(jì)模式中的單例模式來(lái)實(shí)現(xiàn)C++的boost庫(kù)
這篇文章主要介紹了使用設(shè)計(jì)模式中的單例模式來(lái)實(shí)現(xiàn)C++的boost庫(kù)的方法,其中作者對(duì)線(xiàn)程安全格外強(qiáng)調(diào),需要的朋友可以參考下2016-03-03