數據庫連接池的工作原理
連接池的工作原理主要由三部分組成,分別為:
- 連接池的建立
- 連接池中連接的使用管理
- 連接池的關閉
第一、連接池的建立。一般在系統初始化時,連接池會根據系統配置建立,並在池中創建了幾個連接對象,以便使用時能從連接池中獲取。連接池中的連接不能隨意創建和關閉,這樣避免了連接隨意建立和關閉造成的系統開銷。Java中提供了很多容器類可以方便的構建連接池,例如Vector、Stack等。
第二、連接池的管理。連接池管理策略是連接池機制的核心,連接池內連接的分配和釋放對系統的性能有很大的影響。其管理策略是:
當客戶請求數據庫連接時,首先查看連接池中是否有空閑連接,如果存在空閑連接,則將連接分配給客戶使用;如果沒有空閑連接,則查看當前所開的連接數是否已經達到最大連接數,如果沒達到就重新創建一個連接給請求的客戶;如果達到就按設定的最大等待時間進行等待,如果超出最大等待時間,則拋出異常給客戶。
當客戶釋放數據庫連接時,先判斷該連接的引用次數是否超過了規定值,如果超過就從連接池中刪除該連接,否則保留為其他客戶服務。
該策略保證了數據庫連接的有效復用,避免頻繁的建立、釋放連接所帶來的系統資源開銷。
第三、連接池的關閉。當應用程序退出時,關閉連接池中所有的連接,釋放連接池相關的資源,該過程正好與創建相反。
連接池主要參數
使用連接池時,要配置以下參數:
最小連接數:是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費.
最大連接數:是連接池能申請的最大連接數,如果數據庫連接請求超過次數,后面的數據庫連接請求將被加入到等待隊列中,這會影響以后的數據庫操作
最大空閑時間
獲取連接超時時間
超時重試連接次數
數據庫連接池對比
第一、二代連接池
區分一個數據庫連接池是屬於第一代產品還是代二代產品有一個最重要的特征就是看它在架構和設計時采用的線程模型,因為這直接影響的是並發環境下存取數據庫連接的性能。
一般來講采用單線程同步的架構設計都屬於第一代連接池,二采用多線程異步架構的則屬於第二代。比較有代表性的就是Apache Commons DBCP,在1.x版本中,一直延續着單線程設計模式,到2.x才采用多線程模型。
用版本發布時間來辨別區分兩代產品,則一個偷懶的好方法。以下是這些常見數據庫連接池最新版本的發布時間:
從表中可以看出,C3P0已經很久沒有更新了。DBCP更新速度很慢,基本處於不活躍狀態,而Druid和HikariCP處於活躍狀態的更新中,這就是我們說的二代產品了。
二代產品對一代產品的超越是顛覆性的,除了一些“歷史原因”,你很難再找到第二條理由說服自己不選擇二代產品,但任何成功都不是偶然的,二代產品的成功很大程度上得益於前代產品們打下的基礎,站在巨人的肩膀上,新一代的連接池的設計師們將這一項“工具化”的產品,推向了極致。其中,最具代表性的兩款產品是:
- HikariCP
- Druid
徹底死掉的C3P0
C3P0是我使用的第一款數據庫連接池,在很長一段時間內,它一直是Java領域內數據庫連接池的代名詞,當年盛極一時的Hibernate都將其作為內置的數據庫連接池,可以業內對它的穩定性還是認可的。C3P0功能簡單易用,穩定性好這是它的優點,但是性能上的缺點卻讓它徹底被打入冷宮。C3P0的性能很差,差到即便是同時代的產品相比它也是墊底的,更不用和Druid、HikariCP等相比了。正常來講,有問題很正常,改就是了,但c3p0最致命的問題就是架構設計過於復雜,讓重構變成了一項不可能完成的任務。隨着國內互聯網大潮的涌起,性能有硬傷的c3p0徹底的退出了歷史舞台。
咸魚翻身的DBCP
DBCP(DataBase Connection Pool)屬於Apache頂級項目Commons中的核心子項目(最早在Jakarta Commons里就有),在Apache的生態圈中的影響里十分廣泛,比如最為大家所熟知的Tomcat就在內部集成了DBCP,實現JPA規范的OpenJPA,也是默認集成DBCP的。但DBCP並不是獨立實現連接池功能的,它內部依賴於Commons中的另一個子項目Pool,連接池最核心的“池”,就是由Pool組件提供的,因此,DBCP的性能實際上就是Pool的性能,DBCP和Pool的依賴關系如下表:
可以看到,因為核心功能依賴於Pool,所以DBCP本身只能做小版本的更新,真正大版本的更迭則完全依托於pool。有很長一段時間,pool都還是停留在1.x版本,這直接導致DBCP也更新乏力。很多依賴DBCP的應用在遇到性能瓶頸之后,別無選擇,只能將其替換掉,DBCP忠實的擁躉tomcat就在其tomcat 7.0版本中,自己重新設計開發出了一套連接池(Tomcat JDBC Pool)。好在,在2013年事情終於迎來轉機,13年9月Commons-Pool 2.0版本發布,14年2月份,DBCP也終於迎來了自己的2.0版本,基於新的線程模型全新設計的“池”讓DBCP重煥青春,雖然和新一代的連接池相比仍有一定差距,但差距並不大,DBCP2.x版本已經穩穩達到了和新一代產品同級別的性能指標(見下圖)。
DBCP終於靠Pool咸魚翻身,打了一個漂亮的翻身仗,但長時間的等待已經完全消磨了用戶的耐心,與新一代的產品項目相比,DBCP沒有任何優勢,試問,誰會在有選擇的前提下,去選擇那個並不優秀的呢?也許,現在還選擇DBCP2的唯一理由,就是情懷吧。
性能無敵的HikariCP
HikariCP號稱“性能殺手”(It’s Faster),它的表現究竟如何呢,先來看下官網提供的數據:
不光性能強勁,穩定性也不差:
那它是怎么做到如此強勁的呢?官網給出的說明如下:
- 字節碼精簡:優化代碼,直到編譯后的字節碼最少,這樣,CPU緩存可以加載更多的程序代碼;
- 優化代理和攔截器:減少代碼,例如HikariCP的Statement proxy只有100行代碼;
- 自定義數組類型(FastStatementList)代替ArrayList:避免每次get()調用都要進行range check,避免調用remove()時的從頭到尾的掃描;
- 自定義集合類型(ConcurrentBag):提高並發讀寫的效率;
- 其他缺陷的優化,比如對於耗時超過一個CPU時間片的方法調用的研究(但沒說具體怎么優化)。
可以看到,上述這幾點優化,和現在能找到的資料來看,HakariCP在性能上的優勢應該是得到共識的,再加上它自身小巧的身形,在當前的“雲時代、微服務”的背景下,HakariCP一定會得到更多人的青睞。
功能全面的Druid
近幾年,阿里在開源項目上動作頻頻,除了有像fastJson、dubbo這類項目,更有像AliSQL這類的大型軟件,今天說的Druid,就是阿里眾多優秀開源項目中的一個。它除了提供性能卓越的連接池功能外,還集成了SQL監控,黑名單攔截等功能,用它自己的話說,Druid是“為監控而生”。借助於阿里這個平台的號召力,產品一經發布就贏得了大批用戶的擁躉,從用戶使用的反饋來看,Druid也確實沒讓用戶失望。
相較於其他產品,Druid另一個比較大的優勢,就是中文文檔比較全面(畢竟是國人的項目么),在github的wiki頁面,列舉了日常使用中可能遇到的問題,對一個新用戶來講,上面提供的內容已經足夠指導它完成產品的配置和使用了。
下圖為Druid自己提供的性能測試數據:
Druid 相對於其他數據庫連接池的優點
- 強大的監控特性,通過Druid提供的監控功能,可以清楚知道連接池和SQL的工作情況。
a. 監控SQL的執行時間、ResultSet持有時間、返回行數、更新行數、錯誤次數、錯誤堆棧信息;
b. SQL執行的耗時區間分布。什么是耗時區間分布呢?比如說,某個SQL執行了1000次,其中0~1毫秒區間50次,1~10毫秒800次,10~100毫秒100次,100~1000毫秒30次,1~10秒15次,10秒以上5次。通過耗時區間分布,能夠非常清楚知道SQL的執行耗時情況;
c. 監控連接池的物理連接創建和銷毀次數、邏輯連接的申請和關閉次數、非空等待次數、PSCache命中率等。
- 方便擴展。Druid提供了Filter-Chain模式的擴展API,可以自己編寫Filter攔截JDBC中的任何方法,可以在上面做任何事情,比如說性能監控、SQL審計、用戶名密碼加密、日志等等。
- Druid集合了開源和商業數據庫連接池的優秀特性,並結合阿里巴巴大規模苛刻生產環境的使用經驗進行優化。
總結
時至今日,雖然每個應用(需要RDBMS的)都離不開連接池,但在實際使用的時候,連接池已經可以做到“隱形”了。也就是說在通常情況下,連接池完成項目初始化配置之后,就再不需要再做任何改動了。不論你是選擇Druid或是HikariCP,甚至是DBCP,它們都足夠穩定且高效!之前討論了很多關於連接池的性能的問題,但這些性能上的差異,是相較於其他連接池而言的,對整個系統應用來說,第二代連接池在使用過程中體會到的差別是微乎其微的,基本上不存在因為連接池的自身的配飾和使用導致系統性能下降的情況,除非是在單點應用的數據庫負載足夠高的時候(壓力測試的時候),但即便是如此,通用的優化的方式也是單點改集群,而不是在單點的連接池上死扣。
本文參考鏈接:https://blog.csdn.net/CrankZ/article/details/82874158