- HikariCP數據庫連接池實戰
- 朱政科
- 2341字
- 2019-09-02 17:55:05
2.4 主流數據庫連接池對比
英國政治家、歷史學家、畫家、演說家、作家、記者、前首相溫斯頓·丘吉爾曾經說過:“批評也許并不會令人滿意,但卻是必要的。它和人體疼痛的功能類似,它可以喚起人們對于事物不健康狀態的關注?!苯鼛啄辏謾C評測比較熱門,王自如和羅永浩當年關于手機評測就進行過一場辯論。眼花繚亂的數據庫連接池當然也需要一個評測。那么該如何評測呢?評價原則是:性能固然是好的,但是卻不能以放棄可靠性為代價。
2.4.1 性能對比
在本書的主角HikariCP的官網上,通過JMH Benchmarks對主流數據庫連接池分別進行了Connection和Statement級的性能對比,如圖2-13所示。可以看到,常見的數據庫連接池有c3p0、DPCP2、Tomcat、Vibur、Hikari等,HikariCP的性能獨占鰲頭。

圖2-13 Connection和Statement級的性能對比
2.4.2 代碼復雜度
雖然代碼行數并不直接表明復雜性,但冗長和可理解性之間存在著無可爭辯的相關性。一個被業內廣泛接受的觀點是這樣的,每千行代碼的Bug數量在不同的項目和語言中表現得相當一致。除了測試套件外,目視檢查代碼中的邏輯錯誤和競爭條件是最可靠的方法之一。HikariCP從未經歷過記錄的死鎖(deadlock)和活鎖(livelock)的情況。
我們按照截至2014年3月15日已發布的主流數據庫連接池版本,不包括測試和comment,從文件數和代碼量上,我們可以得到對比表格,如表2-2所示。
表2-2 主流數據庫連接池文件數和代碼量對比

上述數據來源于Brett Wooldridge 2014年的文章。寫到這里,不訪和讀者分享一個小故事。筆者曾經在和朋友閑聊過程中分享這組數據,筆者的朋友王振飛先生在2018年4月26日的時候也使用當時最新的TAG對druid和HikariCP的數據做了一次比較。得到的結論如下:
1)只統計Java文件和xml文件,Druid總行數:430289, HikariCP總行數:18372。
2)只統計Java代碼,Druid總行數:428749, HikariCP總行數:17556。
3) 再過濾一下test目錄,Druid總行數:215232, HikariCP總行數:7960。
4)且不說性能,光一個DruidDataSource就編寫了3000行。
一個在某購物網站用戶增長組的好友也曾經告訴我一個我不知道的故事,他知道HikariCP的原因是HikariCP給BoneCP捐了幾美金,BoneCP的作者給他發了郵件。當他在“有贊”的時候,用HikariCP替換Durid,之后,RT出現斷崖式下滑(1.2毫秒~1.5毫秒)并且持續穩定、毛刺少。他還給我分享了當時的性能測試報告。目前,據我所知,“杭州有贊”“51信用卡”“海康威視”3家公司都采用HikariCP作為數據庫連接池,很多中小型互聯網企業也選擇HikariCP作為數據庫連接池。
2.4.3 功能對比
在Druid的官方文檔上,也給出了一份幾種數據庫連接池功能對比表,如表2-3所示。
表2-3 幾種數據庫連接池功能對比

在這份文檔中,Druid強調了如下幾個參數:
1)LRU。LRU是一個性能關鍵指標,特別是Oracle,其中每個Connection對應數據庫端的一個進程,如果數據庫連接池遵從LRU,有助于數據庫服務器優化,這是重要的指標。在測試中,Druid、DBCP、Proxool是遵守LRU的。BoneCP、c3p0則不是。BoneCP在mock環境下性能可能好,但在真實環境中則不見得好。
2)PSCache。PSCache是數據庫連接池的關鍵指標。在Oracle中,類似SELECT NAME FROM USER WHERE ID = ?這樣的SQL,啟用PSCache和不啟用PSCache的性能可能會相差一個數量級。Proxool是不支持PSCache的數據庫連接池,如果你使用Oracle、SQL Server、DB2、Sybase這樣支持游標的數據庫,那就完全不用考慮Proxool。
3)PSCache-Oracle-Optimized。在Oracle 10系列的Driver中,如果開啟PSCache,會占用大量的內存,必須做特別的處理,啟用內部的EnterImplicitCache等方法優化才能夠減少內存的占用。這個功能只有DruidDataSource有。如果你使用的是Oracle JDBC,則應該毫不猶豫地采用DruidDataSource。
4)ExceptionSorter。ExceptionSorter是一個很重要的容錯特性,如果一個連接產生了一個不可恢復的錯誤,必須立刻將其從連接池中去掉,否則會連續產生大量錯誤。這個特性,目前只有JBossDataSource和Druid實現。Druid的實現參考自JBossDataSource,經過長期生產反饋進行了補充。
結合HikariCP和Druid的官方文檔,并收集整理了大量數據庫連接資料,我從線程同步、PSCache、LRU、ExceptionSorted、監控、擴展性、SQL攔截及解析、代碼復雜度、特點、連接池管理、JNDI、Tomcat數據源、更新維護等角度對主流數據庫連接池做了一份更全面的選型對比,以方便讀者在實際學習工作過程中作為工具使用,如表2-4所示。
表2-4 主流數據庫連接池全面功能對比

2.4.4 數據庫中斷
在HikariCP的官方文檔上,記載了關于對比主流數據庫連接池數據庫中斷的測試情況,這是針對各種數據庫連接池都提供的超時功能的測試對比。這個測試實驗模擬了這樣一個場景,將數據庫連接池執行getConnection()在5秒的調用后超時,應用程序應在指定時間內獲得連接,或獲得異常。該實驗設置了一個測試,可以針對遠程MySQL服務器配置4個池(HikariCP、c3p0、DBCP2、Vibur)。每隔2秒getConnection()調用一次,執行查詢,然后關閉連接(返回池)。
每個連接池都如表2-5所示進行了配置,來驗證check-out時的連接。
表2-5 每個數據庫連接池的Validation Enablement配置

每種數據庫連接池超時配置如表2-6所示。
表2-6 每種數據庫連接池超時配置

然后開始運行測試內容,所有的數據庫連接池都沒有問題,但是MySQL服務器網絡電纜突然故意斷開,則出現了如下現象。
?HikariCP:等待5秒,若連接未恢復,拋出SQLExceptions異常,后續getConnection()同樣處理。
?c3p0:完全無反應且無提示,也不會在CheckoutTimeout配置的時長超時后有任何通知給調用者。然后等待2分鐘后終于醒來了,返回一個error。
?Tomcat:返回一個connection,若調用者如果利用這個無效的connection執行SQL語句結果可想而知。大約55秒之后終于醒來,此時getConnection()終于返回一個error,但未像參數配置的那樣等待5秒鐘,而是立即返回error。
?BoneCP:與Tomcat類似,也是約55秒后醒來,有正常反應,且最終等5秒鐘后返回error。
可見,HikariCP的處理方式是最合理的。根據這個測試結果,對于各個CP處理數據庫中斷的情況,評分如表2-7所示。
表2-7 測試結果主觀評分

注意
這是使用JDBC 4.1驅動程序獲得的結果,結果可能因新舊驅動程序差異而有所不同。除了數據庫中斷以外,HikariCP在其他方面還有很多值得稱贊的地方:
?連接的測試在getConnection時同步進行,并有獨特的優化。
?在自己的事務中封裝內部池的查詢,含testQuery一級initSQLQuery。
?當連接Connections歸還給連接池的時候,執行rollback操作。
?在Connection.close的時候,跟蹤并關閉廢棄語句Statements。
?在將Connection連接歸還到客戶端之前清除SQL警告。
?默認參數支持自動提交、事務隔離級別、catalog和只讀狀態。
?對于一些諸如“SQLException對象是否存在斷開連接錯誤”等陷阱進行檢查。