HIkariCP鏈接池比之傳統的Tomcat JDBC datasource 、c3p0 datasource 等傳統鏈接池優勢太大,在獲取鏈接釋放鏈接,執行效率上面高出很多,這個產品的口號是“快速、簡單、可靠”。
官網詳細地說明了HikariCP所做的一些優化,總結如下:
- 字節碼精簡 :優化代碼,直到編譯后的字節碼最少,這樣,CPU緩存可以加載更多的程序代碼;
- 優化代理和攔截器:減少代碼,例如HikariCP的Statement proxy只有100行代碼,只有BoneCP的十分之一;
- 自定義數組類型(FastStatementList)代替ArrayList:避免每次get()調用都要進行range check,避免調用remove()時的從頭到尾的掃描;
- 自定義集合類型(ConcurrentBag:提高並發讀寫的效率;
- 其他針對BoneCP缺陷的優化,比如對於耗時超過一個CPU時間片的方法調用的研究(但沒說具體怎么優化)
HikariCP為什么這么快
JDBC連接池的實現並不復雜,主要是對JDBC中幾個核心對象Connection、Statement、PreparedStatement、CallableStatement以及ResultSet的封裝與動態代理。接下來從幾個方面來看看HikariCP為什么這么快:
優化並精簡字節碼
HikariCP利用了一個第三方的Java字節碼修改類庫Javassist來生成委托實現動態代理。動態代理的實現在ProxyFactory類
之所以使用Javassist生成動態代理,是因為其速度更快,相比於JDK Proxy生成的字節碼更少,精簡了很多不必要的字節碼。
ConcurrentBag:更好的並發集合類實現
ConcurrentBag的實現借鑒於C#中的同名類,是一個專門為連接池設計的lock-less集合,實現了比LinkedBlockingQueue、LinkedTransferQueue更好的並發性能。ConcurrentBag內部同時使用了ThreadLocal和CopyOnWriteArrayList來存儲元素,其中CopyOnWriteArrayList是線程共享的。ConcurrentBag采用了queue-stealing的機制獲取元素:首先嘗試從ThreadLocal中獲取屬於當前線程的元素來避免鎖競爭,如果沒有可用元素則再次從共享的CopyOnWriteArrayList中獲取。此外,ThreadLocal和CopyOnWriteArrayList在ConcurrentBag中都是成員變量,線程間不共享,避免了偽共享(false sharing)的發生。
使用FastList替代ArrayList
FastList是一個List接口的精簡實現,只實現了接口中必要的幾個方法。JDK ArrayList每次調用get()方法時都會進行rangeCheck檢查索引是否越界,FastList的實現中去除了這一檢查,只要保證索引合法那么rangeCheck就成為了不必要的計算開銷(當然開銷極小)。此外,HikariCP使用List來保存打開的Statement,當Statement關閉或Connection關閉時需要將對應的Statement從List中移除。通常情況下,同一個Connection創建了多個Statement時,后打開的Statement會先關閉。ArrayList的remove(Object)方法是從頭開始遍歷數組,而FastList是從數組的尾部開始遍歷,因此更為高效。
Druid和Hikari數據連接池對比:
Druid是java語言中最好的數據庫連接池,並且能夠提供強大的監控和擴展功能
業界把Druid和HikariCP做對比后,雖說HikariCP的性能比Druid高,但是因為Druid包括很多難度的統計和分析功能,所以這也是大家選擇使用它的原因,所以說如果你的項目中不需要這些監控、統計之類的功能,可以選擇速度更快的HikariCP。