如何利用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ó)人自己的開源時(shí)序數(shù)據(jù)庫(kù),性能杠杠滴。
凡此總總,即使沒用過,也聽說過,但大部分人或企業(yè)用的最多的就是白嫖型數(shù)據(jù)庫(kù):mysql。該數(shù)據(jù)庫(kù)的特點(diǎn)就是無論是個(gè)人還是企業(yè)都能玩的起。像Oracle這種名媛型數(shù)據(jù)庫(kù)基本就屬于銀行特供,銀行需要花錢買平安,心里踏實(shí)。不買對(duì)的,只選貴的,因?yàn)槿思掖_實(shí)不差錢。
如果你的后臺(tái)應(yīng)用連數(shù)據(jù)庫(kù)都不需要,那跟咸魚網(wǎng)站有什么區(qū)別呢?就是咸魚二手網(wǎng)也要用到數(shù)據(jù)庫(kù)的。如果一個(gè)IT民工一輩子沒用過數(shù)據(jù)庫(kù)就在35(~45)歲時(shí)“被退休”,那他的職業(yè)生涯是遺憾的,是不完美的,是不純粹的。 好歹年輕是也要用一下非主流的Access吧,哪怕Execel也成。這種感覺就好比在大學(xué)時(shí)沒談過戀愛一樣,光忙著羨慕別人就突然畢業(yè)了。
為什么要搞資源池?
目前大部分后臺(tái)程序都選擇Java開發(fā)或PHP,這兩種語(yǔ)言的第三方庫(kù)非常豐富,豐富到讓開發(fā)人員的只要將精力放在具體業(yè)務(wù)上即可。比如數(shù)據(jù)庫(kù)的資源池,只要選擇好適當(dāng)?shù)膉ar包外加配置好相應(yīng)的數(shù)據(jù)庫(kù)參數(shù),即可放心大膽的使用mysql。
當(dāng)然,如果你命硬的話,也可以選擇用C或C++開發(fā)后臺(tái)應(yīng)用。這時(shí)候你就需要自己DIY一個(gè)數(shù)據(jù)庫(kù)資源池。
如果只是一個(gè)客戶端程序,基本不需要連接池,但對(duì)于后臺(tái)應(yīng)用來說,高并發(fā)就意味著多線程,多線程程就意味著資源的競(jìng)爭(zhēng)。內(nèi)存訪問如此,數(shù)據(jù)庫(kù)訪問也是如此。每次數(shù)據(jù)庫(kù)的打開和關(guān)閉就是一次網(wǎng)絡(luò)連接和關(guān)閉的過程,頻繁的打開和關(guān)閉無疑會(huì)浪費(fèi)大量的系統(tǒng)資源。這時(shí)候就需要提前建立好N個(gè)連接,并放在資源池中并提供給不同線程訪問使用。
mysql資源池實(shí)現(xiàn)的案例源碼
我一直相信好的代碼是不需要過的語(yǔ)言來解釋的,代碼即文檔,要啥自行車。以下案例只是一個(gè)實(shí)現(xiàn)思路,供參考。
頭文件:MysqlPool.h
#pragma warning(disable : 4786)
#include <windows.h>
#include <winsock2.h>
#include <mysql.h> // 確保你的機(jī)器有mysql開發(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ù)查詢回調(diào)函數(shù)
typedef BOOL (CALLBACK *LPFN_RetrieveRecordData)(MYSQL_ROW& sqlRow, MYSQL_FIELD* pSqlFields, int iFieldCount, DWORD dwUserData);
// Mysql數(shù)據(jù)庫(kù)連接類
class CMysqlConn
{
public:
CMysqlConn(const char* pszDBServer, UINT uDBPort, const char* pszDBName,
const char* pszDBUser, const char* pszDBPwd);
virtual ~CMysqlConn();
public:
// 打開/關(guān)閉一個(gè)mysql連接
BOOL Open();
void Close();
// ping連接是否已關(guān)閉
BOOL Ping();
// 重置字符集
BOOL ResetCharset();
public:
// ================SQL語(yǔ)句操作(簡(jiǎn)單實(shí)現(xiàn)幾個(gè))================
// 查詢
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ù)名稱
string m_strDBUser; // 數(shù)據(jù)庫(kù)賬戶
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);
// 銷毀連接池
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ù)連接類
//////////////////////////////////////////////////////////////////////
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();
}
// 打開一個(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); // 斷開連接
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查詢
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ù)連接池類
//////////////////////////////////////////////////////////////////////
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;
}
// 銷毀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í),說明資源池沒有可用mysql連接
{
printf("數(shù)據(jù)庫(kù)沒有可用連接。\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í)間無通信,可能被防火墻關(guān)閉,此時(shí)可以通過mysql_ping函數(shù)測(cè)試一下
// 本例中通過重新設(shè)置字符集
// 重新設(shè)置字符集,并判斷數(shù)據(jù)庫(kù)連接是否已斷開
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("《==資源池說:有借有還再借不難,常來玩啊。\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è)連接,連接池說:記得要還哦!
CMysqlConn* pConn = mysqlPool.Get();
// 假裝做一次數(shù)據(jù)庫(kù)操作
char* pszSQL = "SELECT * FROM CHARACTER_SETS";
pConn->Select(pszSQL, RetrieveRecordData, 0);
// 將連接還給資源池并謝謝!連接池說:不客氣!
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)的二叉樹,所以它沒有使用父指針或者子指針。堆根據(jù)“堆屬性”來排序,“堆屬性”決定了樹中節(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-12
使用設(shè)計(jì)模式中的單例模式來實(shí)現(xiàn)C++的boost庫(kù)
這篇文章主要介紹了使用設(shè)計(jì)模式中的單例模式來實(shí)現(xiàn)C++的boost庫(kù)的方法,其中作者對(duì)線程安全格外強(qiáng)調(diào),需要的朋友可以參考下2016-03-03

