如今幾乎全部的後臺應用都要用到數據庫,什麼關係型的、非關係型的;正當關係的,不正當關係的;主流的和非主流的, 大到Oracle,小到sqlite,以及包括如今逐漸流行的基於物聯網的時序數據庫,好比濤思的TDengine,我們中國人本身的開源時序數據庫,性能槓槓滴。mysql
凡此總總,即便沒用過,也據說過,但大部分人或企業用的最多的就是白嫖型數據庫:mysql。該數據庫的特色就是不管是我的仍是企業都能玩的起。像Oracle這種名媛型數據庫基本就屬於銀行特供,銀行須要花錢買平安,內心踏實。不買對的,只選貴的,由於人家確實不差錢。sql
若是你的後臺應用連數據庫都不須要,那跟鹹魚網站有什麼區別呢?就是鹹魚二手網也要用到數據庫的。若是一個IT民工一生沒用過數據庫就在35(~45)歲時「被退休」,那他的職業生涯是遺憾的,是不完美的,是不純粹的。 好歹年輕是也要用一下非主流的Access吧,哪怕Execel也成。這種感受就比如在大學時沒談過戀愛同樣,光忙着羨慕別人就忽然畢業了。數據庫
目前大部分後臺程序都選擇Java開發或PHP,這兩種語言的第三方庫很是豐富,豐富到讓開發人員的只要將精力放在具體業務上便可。好比數據庫的資源池,只要選擇好適當的jar包外加配置好相應的數據庫參數,便可放心大膽的使用mysql。windows
固然,若是你命硬的話,也能夠選擇用C或C++開發後臺應用。這時候你就須要本身DIY一個數據庫資源池。服務器
若是隻是一個客戶端程序,基本不須要鏈接池,但對於後臺應用來講,高併發就意味着多線程,多線程程就意味着資源的競爭。內存訪問如此,數據庫訪問也是如此。每次數據庫的打開和關閉就是一次網絡鏈接和關閉的過程,頻繁的打開和關閉無疑會浪費大量的系統資源。這時候就須要提早創建好N個鏈接,並放在資源池中並提供給不一樣線程訪問使用。網絡
我一直相信好的代碼是不須要過的語言來解釋的,代碼即文檔,要啥自行車。如下案例只是一個實現思路,供參考。多線程
頭文件:MysqlPool.h併發
#pragma warning(disable : 4786) #include <windows.h> #include <winsock2.h> #include <mysql.h> // 確保你的機器有mysql開發庫 #include <vector> #include <string> using namespace std; #define DEFAULT_POOL_SIZE 20 // 缺省mysql鏈接池中的數量 #define DEFAULT_POOL_TIMEOUT 60 // 獲取池中mysql鏈接的超時 // 自定義數據庫查詢回調函數 typedef BOOL (CALLBACK *LPFN_RetrieveRecordData)(MYSQL_ROW& sqlRow, MYSQL_FIELD* pSqlFields, int iFieldCount, DWORD dwUserData); // Mysql數據庫鏈接類 class CMysqlConn { public: CMysqlConn(const char* pszDBServer, UINT uDBPort, const char* pszDBName, const char* pszDBUser, const char* pszDBPwd); virtual ~CMysqlConn(); public: // 打開/關閉一個mysql鏈接 BOOL Open(); void Close(); // ping鏈接是否已關閉 BOOL Ping(); // 重置字符集 BOOL ResetCharset(); public: // ================SQL語句操做(簡單實現幾個)================ // 查詢 BOOL Select(const char* pszSql, LPFN_RetrieveRecordData lpfnRetrieveRecordData, DWORD dwUserData); // 執行 BOOL Execute(const char* pszSql); // 插入,若是主鍵是自增整型,返回插入後的主鍵值 __int64 Insert(const char* pszSql); protected: MYSQL* m_pMysql; // mysql數據庫操做對象 // 如下是鏈接mysql須要的參數 string m_strDBServer; // mysql數據庫所在服務器 UINT m_uDBPort; // mysql數據庫鏈接端口 string m_strDBName; // 數據庫名稱 string m_strDBUser; // 數據庫帳戶 string m_strDBPwd; // 數據庫密碼 }; // 數據庫鏈接池實現 class CMysqlPool { public: CMysqlPool(); virtual ~CMysqlPool(); // 建立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); // 銷燬鏈接池 void Destroy(); public: // 獲取一個mysql鏈接 CMysqlConn* Get(); // 釋放一個mysql鏈接 void Release(CMysqlConn* pConn); protected: HANDLE m_hSemaphore; // 信號量句柄 DWORD m_dwPoolSize; // 鏈接池大小 DWORD m_dwTimeOut; // 超時,單位秒 CRITICAL_SECTION m_csPool; // 鏈接池鎖 vector<CMysqlConn*> m_vecIdle; // 閒隊列 vector<CMysqlConn*> m_vecBusy; // 忙隊列 };
實現文件:MysqlPool.cpp函數
#include "stdafx.h" #include "MysqlPool.h" #include <assert.h> #include <algorithm> #pragma comment(lib, "libmysql.lib") //鏈接MysQL須要的庫 ////////////////////////////////////////////////////////////////////// // CMysqlConn: mysql數據庫鏈接類 ////////////////////////////////////////////////////////////////////// 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(); } // 打開一個mysql數據庫,即創建一個數據庫鏈接 BOOL CMysqlConn::Open() { if(m_pMysql) { mysql_close(m_pMysql); // 關閉鏈接 m_pMysql = NULL; } m_pMysql = mysql_init(NULL); if(!m_pMysql) return FALSE; // 鏈接數據庫 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; } // 設置重連 char chValue = 1; mysql_options(m_pMysql, MYSQL_OPT_RECONNECT, &chValue); mysql_query(m_pMysql,"set names 'gbk'"); return TRUE; } // 關閉數據庫鏈接 void CMysqlConn::Close() { if(m_pMysql) mysql_close(m_pMysql); // 斷開鏈接 m_pMysql = NULL; } // ping一下mysql,看看鏈接還活着 BOOL CMysqlConn::Ping() { if(m_pMysql) return (0 == mysql_ping(m_pMysql)); return FALSE; } // 設置字符集爲GBK BOOL CMysqlConn::ResetCharset() { if(m_pMysql) return (0 == mysql_query(m_pMysql, "set names 'gbk'")); return FALSE; } // mysql執行: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查詢 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數據庫鏈接池類 ////////////////////////////////////////////////////////////////////// CMysqlPool::CMysqlPool() { ::InitializeCriticalSection(&m_csPool); } CMysqlPool::~CMysqlPool() { Destroy(); ::DeleteCriticalSection(&m_csPool); } // 建立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; // 建立信號量 m_hSemaphore = ::CreateSemaphore(NULL, dwPoolSize, dwPoolSize, NULL); if (NULL == m_hSemaphore) { return FALSE; } // 建立數據庫鏈接池 for(DWORD i = 0; i < dwPoolSize; ++i) { // 建立一個mysql數據庫鏈接 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; } // 銷燬mysql鏈接池 void CMysqlPool::Destroy() { ::CloseHandle(m_hSemaphore); m_hSemaphore = NULL; // 釋放idle隊列 vector<CMysqlConn*>::iterator it; for(it = m_vecIdle.begin(); it != m_vecIdle.end(); ++it) { CMysqlConn* pConn = *it; delete pConn; } m_vecIdle.clear(); // 釋放busy隊列 while(!m_vecBusy.empty()) { CMysqlConn* pConn = m_vecBusy.back(); m_vecBusy.pop_back(); delete pConn; } } // 從mysql鏈接池獲取一個鏈接 CMysqlConn* CMysqlPool::Get() { DWORD dwRet = ::WaitForSingleObject(m_hSemaphore, m_dwTimeOut*1000); if (WAIT_OBJECT_0 != dwRet) // 超時,說明資源池沒有可用mysql鏈接 { printf("數據庫沒有可用鏈接。\r\n"); return NULL; } // 從鏈接池中獲取一個閒置鏈接 CMysqlConn* pConn = NULL; ::EnterCriticalSection(&m_csPool); if (!m_vecIdle.empty()) { pConn = m_vecIdle.back(); // 移出idle隊列 m_vecIdle.pop_back(); m_vecBusy.push_back(pConn); // 加入busy隊列 } ::LeaveCriticalSection(&m_csPool); if(NULL == pConn) return NULL; // 若是一個鏈接長時間無通訊,可能被防火牆關閉,此時能夠經過mysql_ping函數測試一下 // 本例中經過從新設置字符集 // 從新設置字符集,並判斷數據庫鏈接是否已斷開 if(!pConn->ResetCharset()) { if(!pConn->Open()) return NULL; } printf("==》資源池:記得還我哦。\r\n"); return pConn; } // 釋放一個鏈接到mysql鏈接池 void CMysqlPool::Release(CMysqlConn* pConn) { if(NULL == pConn) return; // 釋放一個信號量 ::ReleaseSemaphore(m_hSemaphore, 1, NULL); ::EnterCriticalSection(&m_csPool); // 從Busy隊列中釋放該鏈接 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隊列 m_vecIdle.push_back(pConn); // 加入idle隊列 printf("POOL SIZE : %d, %d\r\n", m_vecIdle.size(), m_vecBusy.size()); } ::LeaveCriticalSection(&m_csPool); printf("《==資源池說:有借有還再借不難,常來玩啊。\r\n"); }
測試函數高併發
void TestMysqlPool() { // 建立mysql鏈接資源池 CMysqlPool mysqlPool; if(!mysqlPool.Create("127.0.0.1", 3306, "information_schema", "root", "123456")) { printf("Create mysql conneticon pool failed.\r\n"); return; } // 從資源池中獲取一個鏈接,鏈接池說:記得要還哦! CMysqlConn* pConn = mysqlPool.Get(); // 僞裝作一次數據庫操做 char* pszSQL = "SELECT * FROM CHARACTER_SETS"; pConn->Select(pszSQL, RetrieveRecordData, 0); // 將鏈接還給資源池並謝謝!鏈接池說:不客氣! mysqlPool.Release(pConn); printf("Test over.\r\n"); }
輸出打印