前言
數(shù)據(jù)庫連接池在Java數(shù)據(jù)庫相關(guān)中間件產(chǎn)品群中墅垮,應(yīng)該算是底層最基礎(chǔ)的一類產(chǎn)品,作為企業(yè)應(yīng)用開發(fā)必不可少的組件痹扇,無數(shù)天才們?yōu)槲覀冐暙I(xiàn)了一個(gè)又一個(gè)的優(yōu)秀產(chǎn)品橡羞,它們有的隨時(shí)代發(fā)展,功成身退拜效,有的則還在不斷迭代喷众,老而彌堅(jiān),更有新生代產(chǎn)品紧憾,或性能無敵到千,或功能全面。接下來赴穗,就讓我們好好聊聊憔四,“那些年,我們用過的數(shù)據(jù)庫連接池”般眉。
第一代連接池
區(qū)分一個(gè)數(shù)據(jù)庫連接池是屬于第一代產(chǎn)品還是第二代產(chǎn)品的一個(gè)最重要特征就是看它在架構(gòu)和設(shè)計(jì)時(shí)采用的線程模型了赵,因?yàn)檫@直接影響的并發(fā)環(huán)境下存取數(shù)據(jù)庫連接的性能。一般來講采用單線程同步的架構(gòu)設(shè)計(jì)的都屬于第一代連接池甸赃,而采用多線程異步架構(gòu)的則屬于第二代柿汛。比較有代表性的就是Apache Commons DBCP,在1.x版本中埠对,一直延續(xù)這單線程設(shè)計(jì)模型络断,到2.x版本才采用多線程模型。
用版本發(fā)布時(shí)間來辨別區(qū)分兩代產(chǎn)品项玛,則一個(gè)偷懶的好方法貌笨。以下是這些常見數(shù)據(jù)庫連接池最新版本的發(fā)布時(shí)間:
數(shù)據(jù)庫連接池 | 最新版本 | 發(fā)布時(shí)間 |
---|---|---|
c3p0 | c3p0-0.9.5.2 | on 9 Dec 2015 |
proxool | 0.9.x | May 24, 2011 |
dbcp | 2.1.1 | 2015-08-06 |
BoneCP | 0.8.0 | on 23 Oct 2013 |
TJP | tomcat9 | Jan 10 2017 |
druid | 1.0.28 | 2017-02-05 |
hikariCP | 2.4.11 | 2017-01-28 |
從表中我們可以看到,c3p0襟沮、DBCP锥惋、Proxool和BoneCP都已經(jīng)很久沒更新了昌腰,TJP(Tomcat JDBC Pool),druid净刮,hikariCPze則仍處于活躍的更新中剥哑,后者明顯就是我們所說的二代產(chǎn)品了。
已經(jīng)徹底死掉的c3p0和proxool
我對(duì)c3p0還是很有感情的淹父,因?yàn)樵谒俏沂褂玫牡谝豢顢?shù)據(jù)庫連接池株婴,在很長一段時(shí)間內(nèi),它一直是Java領(lǐng)域內(nèi)數(shù)據(jù)庫連接池的代名詞暑认,當(dāng)年盛極一時(shí)的Hibernate都將其作為內(nèi)置的數(shù)據(jù)庫連接池困介,可見業(yè)內(nèi)對(duì)它的穩(wěn)定行還是認(rèn)可的。關(guān)于c3p0如何使用蘸际,可以借助于搜索引擎座哩,這里就不再贅述了。c3p0功能簡單易用粮彤,穩(wěn)定性好這是它的優(yōu)點(diǎn)根穷,但性能上的缺點(diǎn)卻讓它徹底被打入冷宮。c3p0的性能很差导坟,差到即便是和同時(shí)代的產(chǎn)品相比屿良,它也是墊底的(見圖一)。同時(shí)代的BoneCP更是直接以干掉它為自己的口號(hào)(官網(wǎng)號(hào)稱比c3p0快25倍)惫周,更不要說和后來的druid和HikariCP相比了尘惧。
正常來講,有問題很正常递递,改就是了喷橙,但c3p0最致命的問題就是架構(gòu)設(shè)計(jì)過于復(fù)雜,讓重構(gòu)變成了一項(xiàng)不可能完成的任務(wù)登舞。隨著國內(nèi)互聯(lián)網(wǎng)大潮的涌起贰逾,性能有硬傷的c3p0徹底的退出了歷史舞臺(tái)。
如果說c3p0被人嫌棄菠秒,是因?yàn)樗陨砑軜?gòu)設(shè)計(jì)的“原罪”似踱,那proxool的冷門,則是與作者興趣的缺失有關(guān)稽煤。proxool最初在設(shè)計(jì)上另辟蹊徑,以JDBC驅(qū)動(dòng)的身份為用戶提供連接池服務(wù)囚戚,這使得將proxool移植到現(xiàn)有代碼中別的十分容易酵熙,而且proxool還開創(chuàng)性的提供了連接池監(jiān)控功能,讓它迅速的獲得了不少用戶的青睞驰坊。
但產(chǎn)品作者興趣的缺失匾二,讓這款本來很有潛力的產(chǎn)品早早夭折。在github的項(xiàng)目首頁,作者寫到:“我從2006年之后就再?zèng)]碰過這個(gè)項(xiàng)目了察藐,我甚至練Java都不用了...”皮璧,也許,proxool本來就是這位天才coder的練手之作分飞,java本身也不是他的主力語言悴务,但不論哪種原因,proxool都已經(jīng)和c3p0一樣譬猫,鮮有人問津了讯檐。
This project is no longer actively maintained. I haven't used Proxool myself since 2006 and no longer even use Java. If the project has any chance of survival at all it would need to find a new maintainer. If anyone can recommend a good alternative I would be happy to mention it here. Alternatively, if anyone wants to contribute to this project then please create a pull request.
咸魚翻身的dbcp
dbcp(DataBase Connection Pool)屬于Apache頂級(jí)項(xiàng)目Commons中的核心子項(xiàng)目(最早在Jakarta Commons里就有),在Apache的生態(tài)圈中的影響里十分廣泛染服,比如最為大家所熟知的tomcat就在內(nèi)部集成了dbcp别洪,實(shí)現(xiàn)JPA規(guī)范的OpenJPA,也是默認(rèn)集成dbcp的柳刮。但dbcp并不是獨(dú)立實(shí)現(xiàn)連接池功能的挖垛,它內(nèi)部依賴于Commons中的另一個(gè)子項(xiàng)目pool,連接池最核心的“池”秉颗,就是由pool組件提供的痢毒,因此,dbcp的性能實(shí)際上就是pool的性能站宗,dbcp和pool的依賴關(guān)系如下表:
Apache Commons DBCP | Apache Commons Pool |
---|---|
v1.2.2 | v1.3 |
v1.3 | v1.5.4 |
v1.4 | v1.5.4 |
v2.0.x | v2.2 |
v2.1.x | v2.4.2 |
可以看到闸准,因?yàn)楹诵墓δ芤蕾囉趐ool,所以dbcp本身只能做小版本的更新梢灭,真正大版本的更迭則完全依托于pool夷家。有很長一段時(shí)間,pool都還是停留在1.x版本敏释,這直接導(dǎo)致dbcp也更新乏力库快。很多依賴dbcp的應(yīng)用在遇到性能瓶頸之后,別無選擇钥顽,只能將其替換掉义屏,dbcp忠實(shí)的擁躉tomcat就在其tomcat 7.0版本中,自己重新設(shè)計(jì)開發(fā)出了一套連接池(Tomcat JDBC Pool)蜂大。好在闽铐,在2013年事情終于迎來轉(zhuǎn)機(jī),13年9月Commons-Pool 2.0版本發(fā)布奶浦,14年2月份兄墅,dbcp也終于迎來了自己的2.0版本,基于新的線程模型全新設(shè)計(jì)的“池”讓dbcp重?zé)ㄇ啻喊牟妫m然和新一代的連接池相比仍有一定差距隙咸,但差距并不大沐悦,dbcp 2.x版本已經(jīng)穩(wěn)穩(wěn)達(dá)到了和新一代產(chǎn)品同級(jí)別的性能指標(biāo)(見下圖)。
dbcp終于靠pool咸魚翻身五督,打了一個(gè)漂亮的翻身仗藏否,但長時(shí)間的等待已經(jīng)完全消磨了用戶的耐心,與新一代的產(chǎn)品項(xiàng)目相比充包,dbcp沒有任何優(yōu)勢(shì)副签,試問,誰會(huì)在有選擇的前提下误证,去選擇那個(gè)并不優(yōu)秀的呢继薛?也許,現(xiàn)在還選擇dbcp2的唯一理由愈捅,就是情懷吧遏考。
甘心赴死的BoneCP
在討論BoneCP這塊的內(nèi)容之前,我們還是先來看看BoneCP作者自己是這么評(píng)價(jià)這款產(chǎn)品的:
BoneCP is a Java JDBC connection pool implementation that is tuned for high performance by minimizing lock contention to give greater throughput for your applications. It beats older connection pools such as C3P0 and DBCP but should now be considered deprecated in favour of HikariCP.
用我自己的話翻譯一下就是:俺是一個(gè)高性能的數(shù)據(jù)庫連接池蓝谨,俺之所以這么牛逼是因?yàn)榘吃趯?shí)現(xiàn)的時(shí)候減少了鎖的使用灌具,想當(dāng)年,什么c3p0啊DBCP啊都被老子干趴了譬巫,但是現(xiàn)在為了支持HikariCP咖楣,俺選擇退出!也就是說芦昔,BoneCP的退出是它自己的選擇诱贿,但它又不像proxool是被拋棄的,它是作者經(jīng)過深思熟慮后咕缎,做出的選擇珠十,可以說BoneCP是“甘心赴死,殺身成仁”凭豪。那么問題來了焙蹭,BoneCP究竟是不是像它自己形容的那樣牛逼?BoneCP和HikariCP之間究竟有啥聯(lián)系嫂伞,能引得它主動(dòng)“金盆洗手”孔厉?
先說性能,BoneCP自稱性能是c3p0的25倍帖努,并提供了依照自己定義的測(cè)試案例撰豺,提供了一組圖片
-
單線程(1,000,000獲得及釋放數(shù)據(jù)庫連接請(qǐng)求,連接池大小20-50)
-
多線程(500線程分別獲取釋放100個(gè)鏈接拼余,連接池大小50-200)
-
多線程(500個(gè)線程每個(gè)100次獲得/釋放郑趁,連接池大小20-500)
當(dāng)然,以上圖片僅供參考姿搜,因?yàn)椴煌膮?shù)配置寡润,不同的應(yīng)用環(huán)境,不同的測(cè)試案例舅柜,得到的結(jié)果肯定也不會(huì)相同梭纹,官網(wǎng)提供的數(shù)據(jù),肯定是在最有利于自己表現(xiàn)的環(huán)境下得到的致份。但結(jié)合另外一份測(cè)試數(shù)據(jù)(第一幅圖)变抽,可以看到BoneCP的性能在第一代產(chǎn)品中,確實(shí)是屬于領(lǐng)先地位的氮块。高性能的表現(xiàn)的秘訣也并不高深绍载,一是極簡的設(shè)計(jì),整個(gè)產(chǎn)品只有幾百k大小滔蝉,二是重構(gòu)內(nèi)部pool的設(shè)計(jì)击儡,減少鎖的使用,而這兩點(diǎn)優(yōu)化原則蝠引,幾乎適用于所以的連接池產(chǎn)品阳谍。
值得一提的是,BoneCP本身并不“健全”螃概,它的很多特征都依賴于Guava矫夯,因此也就和dbcp一樣,面臨更新乏力的問題吊洼。但現(xiàn)在训貌,這些問題都不重要了,因?yàn)樗囈詾榘恋男阅鼙籋ikariCP全面超越冒窍。HikariCP可以說是BoneCP的二代產(chǎn)品(HikariCP自己在官網(wǎng)上聲稱在BoneCP的基礎(chǔ)上递沪,做了很多優(yōu)化),它在設(shè)計(jì)思路上和BoneCP完全一致超燃,主打的特征也是超強(qiáng)的性能表現(xiàn)区拳,關(guān)于HikariCP的詳細(xì)內(nèi)容,我將在下一章節(jié)介紹意乓。
站在巨人肩膀上的第二代連接池
在數(shù)據(jù)庫連接池的產(chǎn)品群中樱调,二代產(chǎn)品對(duì)一代產(chǎn)品的超越是顛覆性的,除了一些“歷史原因”届良,你很難再找到第二條理由說服自己不選擇二代產(chǎn)品笆凌,但任何成功都不是偶然的,二代產(chǎn)品的成功很大程度上得益于前代產(chǎn)品們打下的基礎(chǔ)士葫,站在巨人的肩膀上乞而,新一代的連接池的設(shè)計(jì)師們將這一項(xiàng)“工具化”的產(chǎn)品,推向了極致慢显。其中爪模,最具代表性的兩款產(chǎn)品是:
- HikariCP
- druid
性能無敵的HikariCP
剛剛在介紹BoneCP的時(shí)候多少已經(jīng)提到過HikariCP了欠啤,作為連接池產(chǎn)品中的“性能殺手”,它的表現(xiàn)究竟如何呢屋灌,先來看下官網(wǎng)提供的數(shù)據(jù):
不光性能強(qiáng)勁洁段,穩(wěn)定性也不差:
那它是怎么做到如此強(qiáng)勁的呢?官網(wǎng)給出的說明如下:
- 字節(jié)碼精簡:優(yōu)化代碼共郭,直到編譯后的字節(jié)碼最少祠丝,這樣,CPU緩存可以加載更多的程序代碼除嘹;
- 優(yōu)化代理和攔截器:減少代碼写半,例如HikariCP的Statement proxy只有100行代碼,只有BoneCP的十分之一尉咕;
- 自定義數(shù)組類型(FastStatementList)代替ArrayList:避免每次get()調(diào)用都要進(jìn)行range check叠蝇,避免調(diào)用remove()時(shí)的從頭到尾的掃描;
- 自定義集合類型(ConcurrentBag):提高并發(fā)讀寫的效率龙考;
- 其他針對(duì)BoneCP缺陷的優(yōu)化蟆肆,比如對(duì)于耗時(shí)超過一個(gè)CPU時(shí)間片的方法調(diào)用的研究(但沒說具體怎么優(yōu)化)。
可以看到晦款,上述這幾點(diǎn)優(yōu)化炎功,針對(duì)的都是BoneCP現(xiàn)有的缺陷,優(yōu)化到這份上缓溅,也難怪BoneCP的作者不想玩了蛇损。綜合現(xiàn)在能找到的資料來看,HakariCP在性能上的優(yōu)勢(shì)應(yīng)該是得到共識(shí)的坛怪,再加上它自身小巧的身形淤齐,在當(dāng)前的“云時(shí)代、微服務(wù)”的背景下袜匿,HakariCP一定會(huì)得到更多人的青睞更啄。
功能全面的druid
近幾年,阿里在開源項(xiàng)目上動(dòng)作頻頻居灯,除了有像fastJson這類工具型項(xiàng)目祭务,更有像AliSQL這類的大型軟件,今天說的druid怪嫌,就是阿里眾多優(yōu)秀開源項(xiàng)目中的一個(gè)义锥。它除了提供性能卓越的連接池功能外,還集成了sql監(jiān)控岩灭,黑名單攔截等功能拌倍,用它自己的話說,druid是“為監(jiān)控而生”。借助于阿里這個(gè)平臺(tái)的號(hào)召力柱恤,產(chǎn)品一經(jīng)發(fā)布就贏得了大批用戶的擁躉数初,從用戶使用的反饋來看,druid也確實(shí)沒讓用戶失望膨更。
相較于其他產(chǎn)品妙真,druid另一個(gè)比較大的優(yōu)勢(shì),就是中文文檔比較全面(畢竟是國人的項(xiàng)目么)荚守,在github的wiki頁面,列舉了日常使用中可能遇到的問題练般,對(duì)一個(gè)新用戶來講矗漾,上面提供的內(nèi)容已經(jīng)足夠指導(dǎo)它完成產(chǎn)品的配置和使用了。
下圖為druid自己提供的性能測(cè)試數(shù)據(jù):
最后薄料,隱身的連接池
時(shí)至今日敞贡,雖然每個(gè)應(yīng)用(需要RDBMS的)都離不開連接池,但在實(shí)際使用的時(shí)候摄职,連接池已經(jīng)可以做到“隱形”了誊役。也就是說在通常情況下,連接池完成項(xiàng)目初始化配置之后谷市,就再不需要再做任何改動(dòng)了蛔垢。不論你是選擇druid或是HikariCP,甚至是DBCP迫悠,它們都足夠穩(wěn)定且高效鹏漆!我們之前討論了很多關(guān)于連接池的性能的問題,但這些性能上的差異创泄,是相交于其他連接池而言的艺玲,對(duì)整個(gè)系統(tǒng)應(yīng)用來說,第二代連接池在使用過程中體會(huì)到的差別是微乎其微的鞠抑,基本上不存在因?yàn)檫B接池的自身的配飾和使用導(dǎo)致系統(tǒng)性能下降的情況饭聚,除非是在單點(diǎn)應(yīng)用的數(shù)據(jù)庫負(fù)載足夠高的時(shí)候(壓力測(cè)試的時(shí)候),但即便是如此搁拙,通用的優(yōu)化的方式也是單點(diǎn)改集群秒梳,而不是在單點(diǎn)的連接池上死扣。
與我而言感混,連接池是我打開自己技術(shù)棧(Java存儲(chǔ)層相關(guān))的起點(diǎn)端幼,它有著作為起點(diǎn)的一切優(yōu)點(diǎn):使用廣泛,入門簡單弧满,同時(shí)核心思想和實(shí)踐又足夠有料婆跑。本篇文章僅是一個(gè)開端,既是幫助我(和大家)理清楚市面上這些產(chǎn)品的現(xiàn)狀庭呜,同時(shí)也為為接下來馬上開始的“深入研究”鋪點(diǎn)底子滑进。如果一定要說點(diǎn)什么感悟的話犀忱,那我只能再一次感嘆開源的力量以及社區(qū)對(duì)Java技術(shù)推廣的巨大助力。開源是一種精神扶关,分享是一種態(tài)度阴汇,而這,正是Java語言幾十年依舊屹立不倒的原因节槐。