javascript
Springboot 2.0选择HikariCP作为默认数据库连接池的五大理由
轉(zhuǎn)載自公眾號(hào):工匠小豬豬的技術(shù)世界
摘要: 本文非原創(chuàng),是筆者搜集了一些HikariCP相關(guān)的資料整理給大家的介紹,主要講解了為什么sb2選擇了HikariCP以及HikariCP為什么這么快。
Springboot2默認(rèn)數(shù)據(jù)庫(kù)連接池選擇了HikariCP為何選擇HikariCP理由一、代碼量理由二、口碑理由三、速度理由四、穩(wěn)定性理由五、可靠性HikariCP為什么這么快優(yōu)化并精簡(jiǎn)字節(jié)碼更好的并發(fā)集合類實(shí)現(xiàn)使用FastList替代ArrayListHikariCP與Druid相比哪個(gè)更好?Springboot2快速上手參考資料
Spring Boot 2默認(rèn)數(shù)據(jù)庫(kù)連接池選擇了HikariCP
默認(rèn)的數(shù)據(jù)庫(kù)連接池由Tomcat換成HikariCP. 如果在一個(gè)Tomcat應(yīng)用中用spring.datasource.type來(lái)強(qiáng)制使用Hikari連接池, 則可以去掉這個(gè)override.
為何選擇HikariCP
HiKariCP是數(shù)據(jù)庫(kù)連接池的一個(gè)后起之秀,號(hào)稱性能最好,可以完美地PK掉其他連接池,是一個(gè)高性能的JDBC連接池,基于BoneCP做了不少的改進(jìn)和優(yōu)化。其作者還有另外一個(gè)開(kāi)源作品——高性能的JSON解析器HikariJSON。
它,超快,快到連Spring Boot 2都宣布支持了。
代碼體積更是少的可憐,130kb。
https://github.com/brettwooldridge/HikariJSON
為何要使用HiKariCP?這要先從BoneCP說(shuō)起:
什么?不是有C3P0/DBCP這些成熟的數(shù)據(jù)庫(kù)連接池嗎?一直用的好好的,為什么又搞出一個(gè)BoneCP來(lái)?因?yàn)?#xff0c;傳說(shuō)中BoneCP在快速這個(gè)特點(diǎn)上做到了極致,官方數(shù)據(jù)是C3P0等的25倍左右。不相信?其實(shí)我也不怎么信??墒?#xff0c;有圖有真相啊(圖片來(lái)自BoneCP官網(wǎng):http://jolbox.com/benchmarks.html):
從上述結(jié)果可以看出HikariCP的性能遠(yuǎn)高于c3p0、tomcat等連接池,以致后來(lái)BoneCP作者都放棄了維護(hù),在Github項(xiàng)目主頁(yè)推薦大家使用HikariCP。另外,Spring Boot將在2.0版本中把HikariCP作為其默認(rèn)的JDBC連接池。
PS:需要指出的是,上圖中的數(shù)據(jù)是HikariCP作者對(duì)各個(gè)連接池調(diào)用DataSource.getConnection()、Connection.close()、Connection.prepareStatement()、Statement.execute()、Statement.close()方法的性能測(cè)試結(jié)果。
而且,網(wǎng)上對(duì)于BoneCP是好評(píng)如潮啊,推薦的文章一搜一大堆。
然而,上Maven Repository網(wǎng)站(http://mvnrepository.com/artifact/com.jolbox/bonecp)查找有沒(méi)有最新版本的時(shí)候,你會(huì)發(fā)現(xiàn)最新的是2013年10月份的(這么久沒(méi)新版本出來(lái)了?)。于是,再去BoneCP的Githut(https://github.com/wwadge/bonecp)上看看最近有沒(méi)有提交代碼。卻發(fā)現(xiàn),BoneCP的作者對(duì)于這個(gè)項(xiàng)目貌似已經(jīng)心灰意冷,說(shuō)是要讓步給HikariCP了(有圖有真相):
……什么?又來(lái)一個(gè)CP?……什么是Hikari?
Hikari來(lái)自日文,是“光”(陽(yáng)光的光,不是光禿禿的光)的意思。作者估計(jì)是為了借助這個(gè)詞來(lái)暗示這個(gè)CP速度飛快。不知作者是不是日本人,不過(guò)日本也有很多優(yōu)秀的碼農(nóng),聽(tīng)說(shuō)比特幣據(jù)說(shuō)日本人搞出來(lái)的。。。
這個(gè)產(chǎn)品的口號(hào)是“快速、簡(jiǎn)單、可靠”。實(shí)際情況跟這個(gè)口號(hào)真的匹配嗎?又是有圖有真相(Benchmarks又來(lái)了):
這個(gè)圖,也間接地、再一次地證明了boneCP比c3p0強(qiáng)大很多,當(dāng)然,跟“光”比起來(lái),又弱了不少啊。
那么,這么好的是怎么做到的呢?官網(wǎng)詳細(xì)地說(shuō)明了HikariCP所做的一些優(yōu)化,總結(jié)如下:
- 字節(jié)碼精簡(jiǎn) :優(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ā)讀寫(xiě)的效率;
- 其他針對(duì)BoneCP缺陷的優(yōu)化,比如對(duì)于耗時(shí)超過(guò)一個(gè)CPU時(shí)間片的方法調(diào)用的研究(但沒(méi)說(shuō)具體怎么優(yōu)化)。
很多優(yōu)化的對(duì)比都是針對(duì)BoneCP的……哈哈。參考文章:https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole
理由一、代碼量
幾個(gè)連接池的代碼量對(duì)比(代碼量越少,一般意味著執(zhí)行效率越高、發(fā)生bug的可能性越低):
理由二、口碑
可是,“黃婆賣(mài)瓜,自催自擂”這個(gè)俗語(yǔ)日本人也是懂得,于是,用戶的好評(píng)如潮也是有圖有真相:
理由三、速度
還有第三方關(guān)于速度的測(cè)試:
理由四、穩(wěn)定性
也許你會(huì)說(shuō),速度高,如果不穩(wěn)定也是硬傷啊。于是,關(guān)于穩(wěn)定性的圖也來(lái)了:
理由五、可靠性
另外,關(guān)于可靠性方面,也是有實(shí)驗(yàn)和數(shù)據(jù)支持的。對(duì)于數(shù)據(jù)庫(kù)連接中斷的情況,通過(guò)測(cè)試getConnection(),各種CP的不相同處理方法如下:
(所有CP都配置了跟connectionTimeout類似的參數(shù)為5秒鐘)
- HikariCP:等待5秒鐘后,如果連接還是沒(méi)有恢復(fù),則拋出一個(gè)SQLExceptions 異常;后續(xù)的getConnection()也是一樣處理;
- C3P0:完全沒(méi)有反應(yīng),沒(méi)有提示,也不會(huì)在“CheckoutTimeout”配置的時(shí)長(zhǎng)超時(shí)后有任何通知給調(diào)用者;然后等待2分鐘后終于醒來(lái)了,返回一個(gè)error;
- Tomcat:返回一個(gè)connection,然后……調(diào)用者如果利用這個(gè)無(wú)效的connection執(zhí)行SQL語(yǔ)句……結(jié)果可想而知;大約55秒之后終于醒來(lái)了,這時(shí)候的getConnection()終于可以返回一個(gè)error,但沒(méi)有等待參數(shù)配置的5秒鐘,而是立即返回error;
- BoneCP:跟Tomcat的處理方法一樣;也是大約55秒之后才醒來(lái),有了正常的反應(yīng),并且終于會(huì)等待5秒鐘之后返回error了;
可見(jiàn),HikariCP的處理方式是最合理的。根據(jù)這個(gè)測(cè)試結(jié)果,對(duì)于各個(gè)CP處理數(shù)據(jù)庫(kù)中斷的情況,評(píng)分如下:
參考文章:https://github.com/brettwooldridge/HikariCP/wiki/Bad-Behavior:-Handling-Database-Down
HikariCP為什么這么快
JDBC連接池的實(shí)現(xiàn)并不復(fù)雜,主要是對(duì)JDBC中幾個(gè)核心對(duì)象Connection、Statement、PreparedStatement、CallableStatement以及ResultSet的封裝與動(dòng)態(tài)代理。接下來(lái)從幾個(gè)方面來(lái)看看HikariCP為什么這么快:
優(yōu)化并精簡(jiǎn)字節(jié)碼
HikariCP利用了一個(gè)第三方的Java字節(jié)碼修改類庫(kù)Javassist來(lái)生成委托實(shí)現(xiàn)動(dòng)態(tài)代理。動(dòng)態(tài)代理的實(shí)現(xiàn)在ProxyFactory類,源碼如下:
發(fā)現(xiàn)這些代理方法中只有一行直接拋異常的代碼,注釋寫(xiě)著“Body is replaced (injected) by JavassistProxyFactory”,其實(shí)方法body中的代碼是在編譯時(shí)調(diào)用JavassistProxyFactory才生成的,主要代碼見(jiàn)下圖:
之所以使用Javassist生成動(dòng)態(tài)代理,是因?yàn)槠渌俣雀?#xff0c;相比于JDK Proxy生成的字節(jié)碼更少,精簡(jiǎn)了很多不必要的字節(jié)碼。
ConcurrentBag:更好的并發(fā)集合類實(shí)現(xiàn)
ConcurrentBag的實(shí)現(xiàn)借鑒于C#中的同名類,是一個(gè)專門(mén)為連接池設(shè)計(jì)的lock-less集合,實(shí)現(xiàn)了比LinkedBlockingQueue、LinkedTransferQueue更好的并發(fā)性能。ConcurrentBag內(nèi)部同時(shí)使用了ThreadLocal和CopyOnWriteArrayList來(lái)存儲(chǔ)元素,其中CopyOnWriteArrayList是線程共享的。ConcurrentBag采用了queue-stealing的機(jī)制獲取元素:首先嘗試從ThreadLocal中獲取屬于當(dāng)前線程的元素來(lái)避免鎖競(jìng)爭(zhēng),如果沒(méi)有可用元素則再次從共享的CopyOnWriteArrayList中獲取。此外,ThreadLocal和CopyOnWriteArrayList在ConcurrentBag中都是成員變量,線程間不共享,避免了偽共享(false sharing)的發(fā)生。
使用FastList替代ArrayList
FastList是一個(gè)List接口的精簡(jiǎn)實(shí)現(xiàn),只實(shí)現(xiàn)了接口中必要的幾個(gè)方法。JDK ArrayList每次調(diào)用get()方法時(shí)都會(huì)進(jìn)行rangeCheck檢查索引是否越界,FastList的實(shí)現(xiàn)中去除了這一檢查,只要保證索引合法那么rangeCheck就成為了不必要的計(jì)算開(kāi)銷(當(dāng)然開(kāi)銷極小)。此外,HikariCP使用List來(lái)保存打開(kāi)的Statement,當(dāng)Statement關(guān)閉或Connection關(guān)閉時(shí)需要將對(duì)應(yīng)的Statement從List中移除。通常情況下,同一個(gè)Connection創(chuàng)建了多個(gè)Statement時(shí),后打開(kāi)的Statement會(huì)先關(guān)閉。ArrayList的remove(Object)方法是從頭開(kāi)始遍歷數(shù)組,而FastList是從數(shù)組的尾部開(kāi)始遍歷,因此更為高效。
HikariCP與Druid相比哪個(gè)更好?
有些用戶給了druid這樣的評(píng)論:
不評(píng)論,一個(gè)追求性能,一個(gè)偏向監(jiān)控,直接看之前有人給HikariCP提的關(guān)于跟Druid對(duì)比分析的issue吧。HikariCP作者對(duì)Druid做了測(cè)試并給出了測(cè)試結(jié)果數(shù)據(jù),Druid作者溫少也對(duì)此作了評(píng)論。Issue鏈接:
https://github.com/brettwooldridge/HikariCP/issues/232
筆者個(gè)人的觀點(diǎn)是,hikariCP可以提供監(jiān)控功能的,比如metrics,可以參見(jiàn)筆者的這篇文章 【追光者系列】HikariCP連接池監(jiān)控指標(biāo)實(shí)戰(zhàn)。
另外,監(jiān)控方面,skywalking、pinpoint、mycat這些agent也是可以做到的,以后service mesh普及了更加可以監(jiān)控了,比如sharding-jdbc也可以做監(jiān)控,datamesh,sidecar也可以做監(jiān)控的。
Springboot2快速上手
說(shuō)得這么好,用起來(lái)會(huì)不會(huì)很麻煩啊,會(huì)不會(huì)有很多參數(shù)要配置才能有這樣的效果啊?答案是:不會(huì)。
springboot 2.0 默認(rèn)連接池就是Hikari了,所以引用parents后不用專門(mén)加依賴
配置一下就好
| # jdbc_config datasource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/datebook?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=root # Hikari will use the above plus the following to setup connection pooling spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.maximum-pool-size=15 spring.datasource.hikari.auto-commit=true spring.datasource.hikari.idle-timeout=30000 spring.datasource.hikari.pool-name=DatebookHikariCP spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-test-query=SELECT 1 |
直接啟動(dòng)即可 如圖
參考資料
https://blog.csdn.net/clementad/article/details/46928621
總結(jié)
以上是生活随笔為你收集整理的Springboot 2.0选择HikariCP作为默认数据库连接池的五大理由的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 美团数据仓库-数据脱敏
- 下一篇: Spring Cloud Stream如