前置知識(shí)
關(guān)鍵技術(shù)點(diǎn)
MySQL數(shù)據(jù)庫編程、單例模式、queue隊(duì)列容器、C++11多線程編程、線程互斥、線程同步通信和unique_lock
、基于CAS的原子整形、智能指針shared_ptr、lambda表達(dá)式、生產(chǎn)者-消費(fèi)者線程模型
項(xiàng)目背景
MySQL是一個(gè)基于C/S設(shè)計(jì)的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),一條SQL的執(zhí)行需要通過mysql client發(fā)起一個(gè)連接,經(jīng)過TCP三次握手完成TCP連接,然后再對客戶端進(jìn)行身份驗(yàn)證,驗(yàn)證成功后再把SQL發(fā)給mysql server(RDBMS)執(zhí)行SQL(一般會(huì)涉及磁盤IO),然后給客戶端返回執(zhí)行結(jié)果,執(zhí)行結(jié)束后,進(jìn)行四次揮手?jǐn)嚅_連接
如果想要提高M(jìn)ySQL數(shù)據(jù)庫(基于C/S設(shè)計(jì))的訪問瓶頸:
- 在服務(wù)器端增加緩存服務(wù)器,用戶緩存常用的數(shù)據(jù)(例如redis),減少磁盤IO的次數(shù)
- 還可以使用連接池,來提高M(jìn)ySQL Server的訪問效率
在高并發(fā)情況下:大量的TCP三次握手建立MySQL Server連接認(rèn)證 以及 TCP四次揮手 MySQL Server關(guān)閉連接回收資源所耗費(fèi)的性能時(shí)間也是很明顯的,使用連接池就是為了減少這一部分的性能損耗
連接池功能點(diǎn)介紹
該項(xiàng)目是基于C++語言實(shí)現(xiàn)的連接池,主要也是實(shí)現(xiàn)幾個(gè)所有連接池都支持的通用基礎(chǔ)功能,連接池一般包含了以下內(nèi)容:
- 數(shù)據(jù)庫連接所用的ip地址
- port端口號(hào)
- 用戶名和密碼,連接哪個(gè)庫
- 以及連接池的性能參數(shù),例如 初始連接量,最大連接量,最大空閑時(shí)間、連接超時(shí)時(shí)間等,
1.初始連接量( i n i t S i z e initSize initSize):表示連接池會(huì)預(yù)先和MySQL Server創(chuàng)建 i n i t S i z e initSize initSize個(gè)連接,當(dāng)用戶發(fā)起MySQL訪問時(shí),不需要?jiǎng)?chuàng)建新的連接,直接從連接池中獲取一個(gè)可用的連接就可以,使用完成后,并不去釋放該連接,而是把當(dāng)前連接重新歸還到連接池當(dāng)中
2.最大連接量( m a x S i z e maxSize maxSize):當(dāng)并發(fā)訪問MySQL Server的請求增多時(shí),初始連接量已經(jīng)不夠使用的時(shí)候,此時(shí)會(huì)根據(jù)新的請求數(shù)量去創(chuàng)建更多的連接給用戶去使用,但是新創(chuàng)建的連接數(shù)量上限是 m a x S i z e maxSize maxSize,不能無限制的創(chuàng)建連接。
- 因?yàn)槊恳粋€(gè)連接都會(huì)占用一個(gè) s o c k e t socket socket資源,一般連接池和服務(wù)器程序是部署在一臺(tái)主機(jī)上的,如果連接池占用過多的 s o c k e t socket socket資源,那么服務(wù)器就不能接收太多的客戶端請求了。當(dāng)這些連接使用完成后,再次歸還到連接池當(dāng)中來維護(hù)
3.最大空閑時(shí)間( m a x I d l e T i m e maxIdleTime maxIdleTime):當(dāng)訪問MySQL Server的并發(fā)請求多了以后,連接池里面的連接數(shù)量會(huì)動(dòng)態(tài)增加,上限是 m a x S i z e maxSize maxSize個(gè),當(dāng)這些連接用完再次歸還到連接池當(dāng)中。如果在指定的 m a x I d l e T i m e maxIdleTime maxIdleTime里面,這些新增加的連接都沒有被再次使用過,那么新增加的這些連接資源就要被回收掉,只需要保持初始連接量 i n i t S i z e initSize initSize個(gè)連接就可以了
4.連接超時(shí)時(shí)間( c o n n e c t i o n T i m e o u t connectionTimeout connectionTimeout):當(dāng)MySQL Server的并發(fā)請求量過大,連接池中的連接數(shù)量已經(jīng)到達(dá) m a x S i z e maxSize maxSize了,而此時(shí)沒有空閑的連接可供使用,那么此時(shí)用戶無法從連接池獲取連接,它通過"阻塞"的方式獲取連接的時(shí)間如果超過 c o n n e c t i o n T i m e o u t connectionTimeout connectionTimeout,那么獲取連接失敗,無法訪問數(shù)據(jù)庫
MySQL Server參數(shù)介紹
show variables like 'max_connections'; #查看MySQL Server所支持的最大連接個(gè)數(shù)
超過 m a x _ c o n n e c t i o n s max\_connections max_connections數(shù)量的連接,MySQL Server會(huì)直接拒絕訪問,所以在使用連接池增加連接數(shù)量的時(shí)候,MySQL Server的 m a x c o n n e c t i o n s max_connections maxc?onnections參數(shù)也要適當(dāng)?shù)倪M(jìn)行調(diào)整,以適配連接池的連接上限
功能設(shè)計(jì)
文件 | 功能 |
---|---|
$ConnectionPool.cpp $ 和 C o n n e c t i o n P o o l . h ConnectionPool.h ConnectionPool.h | 實(shí)現(xiàn)連接池 |
C o n n e c t i o n . c p p Connection.cpp Connection.cpp 和 C o n n e c t i o n . h Connection.h Connection.h | 描述每一條建立的連接:數(shù)據(jù)庫操作代碼、增刪改查代碼實(shí)現(xiàn) |
連接池功能點(diǎn)介紹
1.連接池只需要一個(gè)實(shí)例,所以采用單例模式進(jìn)行設(shè)計(jì)
2.服務(wù)器一般是多線程,可能多個(gè)線程都發(fā)起了對這個(gè)Mysql Server的操作請求,都需要從隊(duì)列當(dāng)中獲取連接空閑連接,而 C o n n e c t i o n Connection Connection全部維護(hù)在一個(gè)連接隊(duì)列中 => 所以需要保證連接隊(duì)列的線程安全,需要使用互斥鎖保證隊(duì)列的線程安全
3.如果連接隊(duì)列為空,還需要再獲取連接,此時(shí)需要?jiǎng)討B(tài)創(chuàng)建連接,上限數(shù)量是 m a x S i z e maxSize maxSize
4.連接隊(duì)列中空閑連接時(shí)間超過 m a x I d l e T i m e maxIdleTime maxIdleTime的就要被釋放掉,只保留初始的 i n i t S i z e initSize initSize個(gè)連接就可以了,這個(gè)功能點(diǎn)肯定需要放在獨(dú)立的線程中去做
5.如果連接隊(duì)列為空,而此時(shí)連接的數(shù)量已達(dá)上限 m a x S i z e maxSize maxSize,客戶**“阻塞”**等待 c o n n e c t i o n T i m e o u t connectionTimeout connectionTimeout時(shí)間之后還沒有獲取到空閑的連接,那么獲取連接失敗
- 此處"阻塞"從Connection隊(duì)列獲取空閑連接,可以使用帶超時(shí)時(shí)間的mutex互斥鎖來實(shí)現(xiàn)連接超時(shí)時(shí)間
- 假設(shè)連接超時(shí)時(shí)間為100ms => 并不是直接睡眠100ms,而是在這100ms時(shí)間內(nèi),不斷判斷是否連接隊(duì)列當(dāng)中是否有空閑連接,如果有,那么直接獲取隊(duì)頭的連接,否則連接池隊(duì)列為空,就獲取失敗
6.用戶獲取的連接需要用shared_ptr
智能指針來管理,需要定制刪除器 定制 連接釋放的功能(此處并不是真正釋放連接,而是把連接歸還到連接池中)
7.新連接的生產(chǎn)線程 和 獲取連接的線程 采用生產(chǎn)者-消費(fèi)者線程模型來設(shè)計(jì),所以需要使用線程間的同步通信機(jī)制來保證 => 條件變量和互斥鎖
具體流程
假設(shè)我們的服務(wù)器給客戶提供服務(wù),客戶端發(fā)起請求需要數(shù)據(jù)庫操作時(shí),Server需要到連接池管理的隊(duì)列中獲取一個(gè)連接,然后連接池給Server返回一個(gè)智能指針維護(hù)的連接,Server只管使用這條連接,無需關(guān)心這條連接的釋放,然后使用這條連接去訪問MySQL Server
開發(fā)平臺(tái)選型
有關(guān)MySQL數(shù)據(jù)庫編程、多線程編程、線程互斥和同步通信操作、智能指針、設(shè)計(jì)模式、容器等等這些技術(shù)在C++語言層面都可以直接實(shí)現(xiàn),因此該項(xiàng)目選擇直接在windows平臺(tái)上進(jìn)行開發(fā)
當(dāng)然,因?yàn)椴捎玫亩际钦Z言級(jí)別的接口,沒有強(qiáng)依賴系統(tǒng)的接口,所以跨平臺(tái)性比較好:放在Linux平臺(tái)下用g++也可以直接編譯運(yùn)行,但是需要解決一些庫的依賴問題
關(guān)于MySQL數(shù)據(jù)庫編程
MySQL的windows安裝文件云盤地址如下(下載development開發(fā)版:包含mysql頭文件和libmysql庫文件):
鏈接:https://pan.baidu.com/s/1Y1l7qvpdR2clW5OCdOTwrQ 提取碼:95de
MySQL數(shù)據(jù)庫編程直接采用oracle公司提供的MySQL C/C++客戶端開發(fā)包,在VS上需要進(jìn)行相應(yīng)的頭文件和庫文件的配置,如下
- 1.右鍵項(xiàng)目 - C/C++ - 常規(guī) - 附加包含目錄,填寫下載好的mysql.h頭文件在當(dāng)前電腦的路徑
- 2.右鍵項(xiàng)目 - 鏈接器 - 常規(guī) - 附加庫目錄,填寫libmysql.lib的路徑
- 3.右鍵項(xiàng)目 - 鏈接器 - 輸入 - 附加依賴項(xiàng),填寫libmysql.lib庫的名字
- 4.把 l i b m y s q l . d l l libmysql.dll libmysql.dll動(dòng)態(tài)鏈接庫(Linux下后綴名是.so庫)放在工程目錄下
坑點(diǎn):如果MySQL裝的是64位版本的,所以動(dòng)態(tài)庫什么的都是64位生成的,所以項(xiàng)目先選成64位的
MySQL接口介紹
初始化:
mysql_init()
,
要使用庫,必須先進(jìn)行初始化 由此也可以看出MySQL其實(shí)是網(wǎng)絡(luò)服務(wù),句柄就是文件描述符
MYSQL *my = mysql_init(nullptr);
鏈接數(shù)據(jù)庫
mysql_real_connect
() :文章來源:http://www.zghlxwxcb.cn/news/detail-729061.html
初始化完畢之后,必須先鏈接數(shù)據(jù)庫,再進(jìn)行后續(xù)操作,因?yàn)閙ysql網(wǎng)絡(luò)部分是基于TCP/IP的文章來源地址http://www.zghlxwxcb.cn/news/detail-729061.html
MYSQL *mysql_real_connect
到了這里,關(guān)于【項(xiàng)目】基于C++11實(shí)現(xiàn)的數(shù)據(jù)庫連接池的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!