使用C++實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)連接池
關(guān)鍵技術(shù)點(diǎn)
MySQL數(shù)據(jù)庫(kù)編程、單例模式、queue隊(duì)列容器、C++11多線程編程、線程互斥、線程同步通信和unique_lock、基于CAS的原子整形、智能指針shared_ptr、lambda表達(dá)式、生產(chǎn)者-消費(fèi)者線程模型
連接池項(xiàng)目
為了提高M(jìn)ySQL數(shù)據(jù)庫(kù)(基于C/S設(shè)計(jì))的訪問(wèn)瓶頸,除了在服務(wù)器端增加緩存服務(wù)器緩存常用的數(shù)據(jù)之外(例如redis),還可以增加連接池,來(lái)提高M(jìn)ySQL Server的訪問(wèn)效率,在高并發(fā)情況下,大量的TCP三次握手、MySQL Server連接認(rèn)證、MySQL Server關(guān)閉連接回收資源和TCP四次揮手所耗費(fèi)的性能時(shí)間也是很明顯的,增加連接池就是為了減少這一部分的性能損耗。
在市場(chǎng)上比較流行的連接池包括阿里的druid,c3p0以及apache dbcp連接池,它們對(duì)于短時(shí)間內(nèi)大量的數(shù)據(jù)庫(kù)增刪改查操作性能的提升是很明顯的,但是它們有一個(gè)共同點(diǎn)就是,全部由Java實(shí)現(xiàn)的。
那么本項(xiàng)目就是為了在C/C++項(xiàng)目中,提供MySQL Server的訪問(wèn)效率,實(shí)現(xiàn)基于C++代碼的數(shù)據(jù)庫(kù)連接池模塊。
連接池功能點(diǎn)介紹
連接池一般包含了數(shù)據(jù)庫(kù)連接所用的ip地址、port端口號(hào)、用戶名和密碼以及其它的性能參數(shù),例如初始連接量,最大連接量,最大空閑時(shí)間、連接超時(shí)時(shí)間等,該項(xiàng)目是基于C++語(yǔ)言實(shí)現(xiàn)的連接池,主要也是實(shí)現(xiàn)以上幾個(gè)所有連接池都支持的通用基礎(chǔ)功能。
- 初始連接量(initSize):表示連接池事先會(huì)和MySQL Server創(chuàng)建initSize個(gè)數(shù)的connection連接,當(dāng)應(yīng)用發(fā)起MySQL訪問(wèn)時(shí),不用再創(chuàng)建和MySQL Server新的連接,直接從連接池中獲取一個(gè)可用的連接就可以,使用完成后,并不去釋放connection,而是把當(dāng)前connection再歸還到連接池當(dāng)中。
- 最大連接量(maxSize):當(dāng)并發(fā)訪問(wèn)MySQL Server的請(qǐng)求增多時(shí),初始連接量已經(jīng)不夠使用了,此時(shí)會(huì)根據(jù)新的請(qǐng)求數(shù)量去創(chuàng)建更多的連接給應(yīng)用去使用,但是新創(chuàng)建的連接數(shù)量上限是maxSize,不能無(wú)限制的創(chuàng)建連接,因?yàn)槊恳粋€(gè)連接都會(huì)占用一個(gè)socket資源,一般連接池和服務(wù)器程序是部署在一臺(tái)主機(jī)上的,如果連接池占用過(guò)多的socket資源,那么服務(wù)器就不能接收太多的客戶端請(qǐng)求了。當(dāng)這些連接使用完成后,再次歸還到連接池當(dāng)中來(lái)維護(hù)。
- 最大空閑時(shí)間(maxIdleTime):當(dāng)訪問(wèn)MySQL的并發(fā)請(qǐng)求多了以后,連接池里面的連接數(shù)量會(huì)動(dòng)態(tài)增加,上限是maxSize個(gè),當(dāng)這些連接用完再次歸還到連接池當(dāng)中。如果在指定的maxIdleTime里面,這些新增加的連接都沒(méi)有被再次使用過(guò),那么新增加的這些連接資源就要被回收掉,只需要保持初始連接量initSize個(gè)連接就可以了。
- 連接超時(shí)時(shí)間(connectionTimeout):當(dāng)MySQL的并發(fā)請(qǐng)求量過(guò)大,連接池中的連接數(shù)量已經(jīng)到達(dá)maxSize了,而此時(shí)沒(méi)有空閑的連接可供使用,那么此時(shí)應(yīng)用從連接池獲取連接無(wú)法成功,它通過(guò)阻塞的方式獲取連接的時(shí)間如果超connectionTimeout時(shí)間,那么獲取連接失敗,無(wú)法訪問(wèn)數(shù)據(jù)庫(kù)。
實(shí)現(xiàn)的邏輯圖片
數(shù)據(jù)表的結(jié)構(gòu)
文章內(nèi)容不會(huì)將MySQL的安裝,基于你已經(jīng)下載了mysql server 8.0 ,我們建立一個(gè)mysql數(shù)據(jù)庫(kù)的數(shù)據(jù)表來(lái)演示后面如何用C++連接數(shù)據(jù)表,并且寫(xiě)SQL.
先進(jìn)入mysql,輸入密碼
mysql -u root -p
創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)名叫chat,同時(shí)創(chuàng)建數(shù)據(jù)表
CREATE DATABASE chat; use chat; CREATE TABLE user ( id INT(11) NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT(11) NOT NULL, sex ENUM('male', 'female') NOT NULL, PRIMARY KEY (id) );
如果輸出OK就代表創(chuàng)建user好了,我們來(lái)看看數(shù)據(jù)表
desc user; +-------+-----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | name | varchar(50) | NO | | NULL | | | age | int | NO | | NULL | | | sex | enum('male','female') | NO | | NULL | | +-------+-----------------------+------+-----+---------+----------------+
查看一下內(nèi)容,沒(méi)有
mysql> select * from user; Empty set (0.19 sec)
到這里我們MySQL表就創(chuàng)建好了,我們不用管他,我們進(jìn)行編寫(xiě)CPP連接數(shù)據(jù)庫(kù)代碼
連接數(shù)據(jù)庫(kù),并且執(zhí)行sql語(yǔ)句
打開(kāi)VS2019,并且創(chuàng)建一個(gè)控制臺(tái)項(xiàng)目,項(xiàng)目結(jié)構(gòu)如圖
main.cpp負(fù)責(zé)執(zhí)行主函數(shù)代碼,Connect負(fù)責(zé)編寫(xiě)封裝數(shù)據(jù)庫(kù)的連接和sql操作,mysqlPool負(fù)責(zé)編寫(xiě)數(shù)據(jù)庫(kù)連接池。
但我們還不急著編寫(xiě)代碼,先導(dǎo)入需要的外部庫(kù),在VS上需要進(jìn)行相應(yīng)的頭文件和庫(kù)文件的配置,如下:
- 1.右鍵項(xiàng)目 - C/C++ - 常規(guī) - 附加包含目錄,填寫(xiě)mysql.h頭文件的路徑
- 2.右鍵項(xiàng)目 - 鏈接器 - 常規(guī) - 附加庫(kù)目錄,填寫(xiě)libmysql.lib的路徑
- 3.右鍵項(xiàng)目 - 鏈接器 - 輸入 - 附加依賴項(xiàng),填寫(xiě)libmysql.lib庫(kù)的名字
- 4.把libmysql.dll動(dòng)態(tài)鏈接庫(kù)(Linux下后綴名是.so庫(kù))放在工程目錄下
如果你沒(méi)有修改過(guò)MySQL路徑,一般mysql.h在你的電腦路徑如下
如果你沒(méi)有修改過(guò)MySQL路徑,一般libmysql.lib在你的電腦路徑如下
libmysql.dll文件存放在你項(xiàng)目文件路徑下面
封裝Mysql.h的接口成connection類
接下來(lái)封裝一下mysql的數(shù)據(jù)庫(kù)連接代碼,不懂的看看注釋,也很簡(jiǎn)單的調(diào)用Mysql.h的接口,我們?cè)赾onnection中額外加入創(chuàng)建時(shí)間函數(shù)和存活時(shí)間函數(shù),不能讓空閑的線程存活時(shí)間超過(guò)定義的最大空閑時(shí)間
connect.h的代碼如下
#pragma once #include <mysql.h> #include <string> #include <ctime> using namespace std; /* 封裝MySQL數(shù)據(jù)庫(kù)的接口操作 */ class Connection { public: // 初始化數(shù)據(jù)庫(kù)連接 Connection(); // 釋放數(shù)據(jù)庫(kù)連接資源 ~Connection(); // 連接數(shù)據(jù)庫(kù) bool connect(string ip, unsigned short port, string user, string password, string dbname); // 更新操作 insert、delete、update bool update(string sql); // 查詢操作 select MYSQL_RES* query(string sql); // 刷新一下連接的起始的空閑時(shí)間點(diǎn) void refreshAliveTime() { _alivetime = clock(); } // 返回存活的時(shí)間 clock_t getAliveeTime()const { return clock() - _alivetime; } private: MYSQL* _conn; // 表示和MySQL Server的一條連接 clock_t _alivetime; // 記錄進(jìn)入空閑狀態(tài)后的起始存活時(shí)間 };
connect.cpp的代碼如下
include "public.h" #include "Connect.h" #include <iostream> using namespace std; Connection::Connection() { // 初始化數(shù)據(jù)庫(kù)連接 _conn = mysql_init(nullptr); } Connection::~Connection() { // 釋放數(shù)據(jù)庫(kù)連接資源 if (_conn != nullptr) mysql_close(_conn); } bool Connection::connect(string ip, unsigned short port, string username, string password, string dbname) { // 連接數(shù)據(jù)庫(kù) MYSQL* p = mysql_real_connect(_conn, ip.c_str(), username.c_str(), password.c_str(), dbname.c_str(), port, nullptr, 0); return p != nullptr; } bool Connection::update(string sql) { // 更新操作 insert、delete、update if (mysql_query(_conn, sql.c_str())) { LOG(+ "更新失敗:" + sql); return false; } return true; } MYSQL_RES* Connection::query(string sql) { // 查詢操作 select if (mysql_query(_conn, sql.c_str())) { LOG("查詢失敗:" + sql); return nullptr; } return mysql_use_result(_conn); }
在public.h中編寫(xiě)的代碼,幫助我們輸出日志和警告
#pragma once #include <iostream> #define LOG(str) \ std::cout << __FILE__ << ":"<<__LINE__<<" " \ __TIMESTAMP__ << ":"<<str <<std::endl;
使用這個(gè)宏,你可以在代碼中的任何地方輕松輸出日志信息。
我們暫時(shí)用這個(gè)main先測(cè)試下connection類
main.cpp代碼
#include <iostream> #include "Connect.h" int main() { Connection conn; char sql[1024] = { 0 }; //插入一條數(shù)據(jù) sprintf(sql, "insert into user(name,age,sex) values('%s','%d','%s');", "zhang san", 20, "male"); conn.connect("127.0.0.1", 3306, "root", "123456", "chat"); conn.update(sql); //更新sql語(yǔ)句 return 0; }
如果你的vs2019給你報(bào)安全警告,應(yīng)該是sprintf的問(wèn)題,你右擊項(xiàng)目,選擇屬性中C++的常規(guī)中SDL檢查,設(shè)置為否。編譯運(yùn)行后,我們返回MySQL的界面,發(fā)現(xiàn)數(shù)據(jù)已經(jīng)插入成功了
mysql> select * from user; +----+-----------+-----+------+ | id | name | age | sex | +----+-----------+-----+------+ | 1 | zhang san | 20 | male | +----+-----------+-----+------+ 1 row in set (0.02 sec)
現(xiàn)在我們已經(jīng)成功能調(diào)用外部接口來(lái)連接Mysql數(shù)據(jù)庫(kù)了,接下來(lái)我們來(lái)編寫(xiě)連接池
.
編寫(xiě)連接池
MySQL配置文件和加載配置文件
我們來(lái)編寫(xiě)mySqlPool的代碼,因?yàn)閿?shù)據(jù)庫(kù)連接池只有一個(gè),所以我們寫(xiě)成單例模式。同時(shí)會(huì)有多個(gè)服務(wù)端進(jìn)入連接池,所以我們要添加互斥鎖來(lái)避免線程之間的沖突。
我們?cè)陧?xiàng)目中創(chuàng)建一個(gè)名叫mysql.ini
配置文件存儲(chǔ)數(shù)據(jù)庫(kù)連接的信息,例如數(shù)據(jù)庫(kù)ip地址,用戶名,密碼等
mysql.ini
的內(nèi)容如下,如果你的用戶名和密碼跟里面不同,請(qǐng)修改
#數(shù)據(jù)庫(kù)連接池的配置文件 ip=127.0.0.1 port=3306 username=root password=123456 initSize=10 maxSize=1024 #最大空閑時(shí)間默認(rèn)單位為秒 maxIdleTime=60 #連接超時(shí)時(shí)間單位是毫米 connectionTimeOut=100
我們把mysqlPool.h文件中需要的函數(shù)都聲明好,等會(huì)在cpp中實(shí)現(xiàn)。
#pragma once #include "public.h" #include "Connect.h" #include <queue> #include <mutex> #include <string> #include <atomic> #include <memory> #include <functional> #include <condition_variable> //因?yàn)閿?shù)據(jù)庫(kù)連接池子只有一個(gè),所以我們采用單例模式 class mySqlPool { public: //獲取連接池對(duì)象實(shí)例 static mySqlPool* getMySqlPool(); std::shared_ptr<Connection> getConnection();//從連接池獲取一個(gè)可用的空閑連接 private: mySqlPool();//構(gòu)造函數(shù)私有化 bool loadConfigFile();//從配置文件中加載配置項(xiàng) void produceConnectionTask(); //運(yùn)行在獨(dú)立的線程中,專門(mén)負(fù)責(zé)生產(chǎn)新連接 //掃描超過(guò)maxIdleTime時(shí)間的空閑連接,進(jìn)行隊(duì)列的連接回收 void scannerConnectionTask(); std::string _ip;//mysql的ip地址 std::string _dbname;//數(shù)據(jù)庫(kù)的名稱 unsigned short _port; //mysql端口號(hào)3306 std::string _username;//mysql用戶名 std::string _password;//mysql登陸密碼 int _initSize;//連接池的初始連接量 int _maxSize;//連接池的最大連接量 int _maxIdleTime;//連接池最大空閑時(shí)間 int _connectionTimeOut;//連接池獲取連接的超時(shí)時(shí)間 std::queue<Connection*> _connectionQue;//存儲(chǔ)mysql連接隊(duì)列 std::mutex _queueMutex; //維護(hù)連接隊(duì)列的線程安全互斥鎖 std::atomic_int _connectionCnt; //記錄連接所創(chuàng)建的connect的數(shù)量 std::condition_variable cv;//設(shè)置條件變量,用于生產(chǎn)者線程和消費(fèi)者線程的通信 };
編寫(xiě)mySqlPool.cpp 中加載我們上面.ini配置文件
的函數(shù)
//在mySqlPool.cpp中 //加載配置文件 bool mySqlPool::loadConfigFile() { FILE* pf = fopen("mysql.ini", "r"); if (pf == nullptr) { LOG("mysql.ini file is not exits!"); return false; } while (!feof(pf)) //遍歷配置文件 { char line[1024] = { 0 }; fgets(line, 1024, pf); std::string str = line; int idx = str.find('=', 0); //從0開(kāi)始找'='符號(hào)的位置 if (idx == -1)continue; int endidx = str.find('\n', idx);//從idx尋找'\n'的位置,也就是末尾 std::string key = str.substr(0, idx); //獲取配置文件中=號(hào)左邊的key //從等號(hào)后到末尾,剛好是value的string形式 std::string value = str.substr(idx + 1, endidx - idx - 1); if (key == "ip") { _ip = value; } else if (key == "port") { //字符串轉(zhuǎn)換成unsigned short _port = static_cast<unsigned short>(std::stoul(value)); } else if (key == "username") { _username = value; } else if (key == "password") { _password = value; } else if (key == "dbname") { _dbname = value; } else if (key == "initSize") { _initSize = std::stoi(value); } else if (key == "maxSize") { _maxSize = std::stoi(value); } else if (key == "maxIdleTime") { _maxIdleTime = std::stoi(value); } else if (key == "connectionTimeOut") { _connectionTimeOut = std::stoi(value); } } return true; }
這樣我們加載配置文件就完成了
編寫(xiě)連接池單例模式
單例模式確保數(shù)據(jù)庫(kù)連接池在整個(gè)應(yīng)用程序中只有一個(gè)實(shí)例。這樣,所有需要數(shù)據(jù)庫(kù)連接的線程或操作都可以從這個(gè)池中獲取連接,而不是每次都創(chuàng)建新的連接。這大大減少了資源消耗和性能損耗。(如果不懂?dāng)?shù)據(jù)模式單例模式可以百度一下)
我們?cè)?h文件中,我們先將構(gòu)造函數(shù)private化,這樣外部就只能通過(guò)接口來(lái)獲取,我們?cè)赾pp中來(lái)編寫(xiě)具體的實(shí)現(xiàn)代碼
構(gòu)造方法
//mySqlPool.h //構(gòu)造方法 mySqlPool::mySqlPool() { if (!loadConfigFile()) { LOG("load Config File is error!"); return; } //創(chuàng)建初始數(shù)量的連接 for (int i = 0; i < _initSize; ++i) { Connection* p = new Connection(); p->connect(_ip, _port, _username, _password, _dbname); } //啟動(dòng)一個(gè)新線程,作為連接的生產(chǎn)者 std::thread produce(std::bind(&mySqlPool::produceConnectionTask, this)); produce.detach(); //啟動(dòng)一個(gè)新線程,作為空閑連接超時(shí)的回收者 std::thread scanner(std::bind(&mySqlPool::scannerConnectionTask, this)); scanner.detach(); }
單例模式
//mySqlPool.h //單例模式 mySqlPool* mySqlPool::getMySqlPool() { static mySqlPool pool; return &pool; }
現(xiàn)在我們已經(jīng)成功的編寫(xiě)了單例模式,接下來(lái)我們開(kāi)始獲取數(shù)據(jù)庫(kù)的連接。
數(shù)據(jù)庫(kù)連接的線程通信
我們創(chuàng)建一個(gè)connect*線程隊(duì)列queue
來(lái)存放MySQL數(shù)據(jù)庫(kù)的連接connect
,同時(shí)我們還會(huì)額外創(chuàng)建兩個(gè)線程。
一個(gè)線程是生產(chǎn)者
,開(kāi)始從Connect類中獲取initSize
個(gè)連接加入連接隊(duì)列中準(zhǔn)備著,當(dāng)判斷連接隊(duì)列empty,又開(kāi)始獲取連接加入連接隊(duì)列中 ,如果不為empty就進(jìn)入阻塞狀態(tài)。
生產(chǎn)者線程代碼
//運(yùn)行在獨(dú)立的線程中,專門(mén)負(fù)責(zé)生產(chǎn)新連接 void mySqlPool::produceConnectionTask() { while (true) { std::unique_lock<std::mutex> lock(_queueMutex); while (!_connectionQue.empty()) cv.wait(lock); //隊(duì)列不為空不生產(chǎn)線程 //沒(méi)有到上線就可以生產(chǎn)線程 if (_connectionCnt < _maxSize) { auto p = new Connection(); p->connect(_ip, _port, _username, _password, _dbname); p->refreshAliveTime();//創(chuàng)建的時(shí)候刷新存活時(shí)間 _connectionQue.push(p); ++_connectionCnt; } cv.notify_all(); } }
另外一個(gè)線程是消費(fèi)者,如果服務(wù)端想要獲取隊(duì)列中的連接,消費(fèi)者線程將會(huì)從隊(duì)列中拿出connection來(lái),如果隊(duì)列為empty,線程會(huì)處于阻塞狀態(tài)。
消費(fèi)者線程代碼
/從連接池獲取一個(gè)可用的空閑連接 std::shared_ptr<Connection> mySqlPool::getConnection() { std::unique_lock<std::mutex> lock(_queueMutex); while (_connectionQue.empty()) { //如果超時(shí)沒(méi)有獲取可用的空閑連接返回空 if (std::cv_status::timeout == cv.wait_for(lock, std::chrono::milliseconds(100))) if (_connectionQue.empty()) { LOG("get Connection error"); return nullptr; } } std::shared_ptr<Connection> sp(_connectionQue.front(), [&](Connection* pcon) { //保證只能同一時(shí)刻只能有一個(gè)線程歸還連接給隊(duì)列 std::unique_lock<std::mutex> lock(_queueMutex); pcon->refreshAliveTime();//創(chuàng)建的時(shí)候刷新存活時(shí)間 _connectionQue.push(pcon); }); _connectionQue.pop(); cv.notify_all(); return sp; }
如果隊(duì)列里面大于初始個(gè)數(shù)的新connection空閑時(shí)間大于最大空閑時(shí)間,我們將會(huì)回收該連接(但是不會(huì)完全釋放,我們將其歸還在連接池中)。
上面getConnection代碼的這段就是實(shí)現(xiàn)了回收功能
std::shared_ptr<Connection> sp(_connectionQue.front(), [&](Connection* pcon) { //保證只能同一時(shí)刻只能有一個(gè)線程歸還連接給隊(duì)列 std::unique_lock<std::mutex> lock(_queueMutex); pcon->refreshAliveTime();//創(chuàng)建的時(shí)候刷新存活時(shí)間 _connectionQue.push(pcon); });
掃描超過(guò)maxIdleTime時(shí)間的空閑連接,進(jìn)行隊(duì)列的連接回收
//連接線程回收 void mySqlPool::scannerConnectionTask() { while (true) { //通過(guò)sleep模擬定時(shí)效果,每_maxIdleTime檢查一次 std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime)); //掃描整個(gè)隊(duì)列釋放多余的超時(shí)連接 std::unique_lock<std::mutex> lock(_queueMutex); while (_connectionCnt > _initSize) { auto p = _connectionQue.front(); if (p->getAliveeTime() >= (_maxIdleTime * 1000)) { _connectionQue.pop(); delete p;//這里會(huì)調(diào)用智能指針,回收到隊(duì)列中 } } } }
到這里,我們連接池的代碼已經(jīng)完成了,接下來(lái)是測(cè)試一下代碼
連接池的壓力測(cè)試
我們分別測(cè)試連接個(gè)數(shù)為10,100,1000時(shí)候的性能差異,創(chuàng)建一個(gè)test.h文件,編寫(xiě)測(cè)試代碼
注意下面的測(cè)試可能根據(jù)不同的電腦性能,可能速度會(huì)有所差異。
普通連接
//test.h //非線程池的連接 void testSql( int n) { clock_t begin = clock(); std::thread t([&n]() { for (int i = 1; i < n; ++i) { Connection cnn; char sql[1024] = { 0 }; sprintf(sql, "insert into user(name,age,sex) values('%s',%d,'%s')", "zhang san", 20, "male"); cnn.connect("127.0.0.1", 3306, "root", "123456", "chat"); cnn.update(sql); }}); t.join(); clock_t end = clock(); std::cout << "普通連接數(shù)量為:" << n << "的sql執(zhí)行時(shí)間:" << (end - begin) << "ms" << std::endl; }
main.cpp中調(diào)用
#include <iostream> #include "Connect.h" #include "mySqlPool.h" #include "test.h" int main() { testSql(10);//普通連接數(shù)量為 : 10的sql執(zhí)行時(shí)間 : 2838ms testSql(100);//普通連接數(shù)量為 : 100的sql執(zhí)行時(shí)間: 12299 testSql(1000);//普通連接數(shù)量為 : 1000的sql執(zhí)行時(shí)間 : 104528ms return 0; }
單線程的線程池
//test.h void f(int n) { mySqlPool* cp = mySqlPool::getMySqlPool(); for (int i = 1; i <= n; ++i) { std::shared_ptr<Connection> sp = cp->getConnection(); char sql[1024] = { 0 }; sprintf(sql, "insert into user(name,age,sex) values('%s',%d,'%s')", "zhang san", 20, "male"); sp->update(sql); } } //測(cè)試連接池連接 void testSqlPool(int n) { clock_t begin = clock(); std::thread t1(f, n); t1.join(); clock_t end = clock(); std::cout << "單線程采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:" << n << "的sql執(zhí)行時(shí)間:" << (end - begin) << "ms" << std::endl; }
main.cpp中調(diào)用
#include <iostream> #include "Connect.h" #include "mySqlPool.h" #include "test.h" int main() { testSqlPool(10);//單線程 采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:10的sql執(zhí)行時(shí)間:1745ms testSqlPool(100);//單線程 采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:100的sql執(zhí)行時(shí)間:9779ms testSqlPool(1000);//單線程 采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:1000的sql執(zhí)行時(shí)間 : 86016ms return 0; }
多線程的線程池
//test.h //測(cè)試連接池連接 4線程 void testSqlPool4(int n) { int n2 = n / 4; clock_t begin = clock(); std::thread t1(f, n2); std::thread t2(f, n2); std::thread t3(f, n2); std::thread t4(f, n2); t1.join(); t2.join(); t3.join(); t4.join(); clock_t end = clock(); std::cout << "四線程采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:" << n << "的sql執(zhí)行時(shí)間:" << (end - begin) << "ms" << std::endl; }
main.cpp中調(diào)用
#include <iostream> #include "Connect.h" #include "mySqlPool.h" #include "test.h" int main() { testSqlPool4(100);//4條線程 采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:100的sql執(zhí)行時(shí)間 : 3715ms testSqlPool4(1000);//4條線程 采用數(shù)據(jù)庫(kù)連接池,連接數(shù)量為:1000的sql執(zhí)行時(shí)間 : 34686ms return 0; }
由上面測(cè)試數(shù)據(jù)可以得出,普通連接<單線程連接池<多線程連接池,連接池比普通連接還是優(yōu)化很多的。
如果看完了還是對(duì)于代碼很陌生,可以下載來(lái)看一看。源碼地址
以上就是使用C++實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)連接池的詳細(xì)內(nèi)容,更多關(guān)于C++ MySQL數(shù)據(jù)庫(kù)連接池的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言使用鏈表實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言使用鏈表實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12C語(yǔ)言結(jié)構(gòu)體內(nèi)存的對(duì)齊知識(shí)詳解
這篇文章主要介紹了C語(yǔ)言結(jié)構(gòu)體內(nèi)存的對(duì)齊的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03C語(yǔ)言學(xué)習(xí)進(jìn)階篇之萬(wàn)字詳解指針與qsort函數(shù)
之前的指針詳解中,提到過(guò)qsort函數(shù),這個(gè)函數(shù)是用來(lái)排序的,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言指針與qsort函數(shù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08Qt實(shí)現(xiàn)導(dǎo)出QTableWidget/QTableView數(shù)據(jù)
這篇文章主要介紹了在Qt中實(shí)現(xiàn)將QTableWidget或者QTableView中的數(shù)據(jù)直接導(dǎo)出的示例代碼,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-01-01C語(yǔ)言實(shí)現(xiàn)飛機(jī)游戲(2)
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)飛機(jī)游戲的第二部分,進(jìn)行功能完善,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05C語(yǔ)言中pthread_exit()函數(shù)實(shí)現(xiàn)終止線程
本文主要介紹了C語(yǔ)言中pthread_exit()函數(shù)實(shí)現(xiàn)終止線程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05MFC串口通信發(fā)送16進(jìn)制數(shù)據(jù)的方法
這篇文章主要為大家詳細(xì)介紹了MFC串口通信發(fā)送16進(jìn)制數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01