數據庫連接池了解和常用連接池對比


一、.背景介紹

什么是連接池
1)先講一下“池”, 池(Pool)技術在一定程度上可以明顯優化服務器應用程序的性能,提高程序執行效率和降低系統資源開銷。這里所說的池是一種廣義上的池,比如數據庫連接池、線程池、內存池、對象池等。其中,對象池可以看成保存對象的容器,在進程初始化時創建一定數量的對象。需要時直接從池中取出一個空閑對象,用完后並不直接釋放掉對象,而是再放到對象池中以方便下一次對象請求可以直接復用。其他幾種池的設計思想也是如此,池技術的優勢是,可以消除對象創建所帶來的延遲,從而提高系統的性能。

為什么要使用連接池
 數據庫連接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤為突出。  一個數據庫連接對象均對應一個物理數據庫連接,每次操作都打開一個物理連接,使用完都關閉連接,這樣造成系統的 性能低下。 數據庫連接池的解決方案是在應用程序啟動時建立足夠的數據庫連接,並講這些連接組成一個連接池(簡單說:在一個“池”里放了好多半成品的數據庫聯接對象),由應用程序動態地對池中的連接進行申請、使用和釋放。對於多於連接池中連接數的並發請求,應該在請求隊列中排隊等待。並且應用程序可以根據池中連接的使用率,動態增加或減少池中的連接數。 連接池技術盡可能多地重用了消耗內存地資源,大大節省了內存,提高了服務器地服務效率,能夠支持更多的客戶服務。通過使用連接池,將大大提高程序運行效率,同時,我們可以通過其自身的管理機制來監視數據庫連接的數量、使用情況等
二,普通數據庫鏈接和連接池的優劣對比

不使用數據庫連接池的步驟:

  1. TCP建立連接的三次握手
  2. MySQL認證的三次握手
  3. 真正的SQL執行
  4. MySQL的關閉
  5. TCP的四次握手關閉

可以看到,為了執行一條SQL,卻多了非常多我們不關心的網絡交互。

優點:
實現簡單
缺點:
網絡IO較多
數據庫的負載較高
響應時間較長及QPS較低
應用頻繁的創建連接和關閉連接,導致臨時對象較多,GC頻繁
在關閉連接后,會出現大量TIME_WAIT 的TCP狀態(在2個MSL之后關閉)

使用連接池流程:

第一次訪問的時候,需要建立連接。 但是之后的訪問,均會復用之前創建的連接,直接執行SQL語句。

優點:

  1. 較少了網絡開銷
  2. 系統的性能會有一個實質的提升
  3. 沒了麻煩的TIME_WAIT狀態

三、.數據庫連接池的工作原理

連接池的工作原理主要由三部分組成,分別為

  1. 連接池的建立
  2. 連接池中連接的使用管理
  3. 連接池的關閉

第一、連接池的建立。一般在系統初始化時,連接池會根據系統配置建立,並在池中創建了幾個連接對象,以便使用時能從連接池中獲取。連接池中的連接不能隨意創建和關閉,這樣避免了連接隨意建立和關閉造成的系統開銷。Java中提供了很多容器類可以方便的構建連接池,例如Vector、Stack等。

第二、連接池的管理。連接池管理策略是連接池機制的核心,連接池內連接的分配和釋放對系統的性能有很大的影響。其管理策略是:

        當客戶請求數據庫連接時,首先查看連接池中是否有空閑連接,如果存在空閑連接,則將連接分配給客戶使用;如果沒有空閑連接,則查看當前所開的連接數是否已經達到最大連接數,如果沒達到就重新創建一個連接給請求的客戶;如果達到就按設定的最大等待時間進行等待,如果超出最大等待時間,則拋出異常給客戶。

        當客戶釋放數據庫連接時,先判斷該連接的引用次數是否超過了規定值,如果超過就從連接池中刪除該連接,否則保留為其他客戶服務。

        該策略保證了數據庫連接的有效復用,避免頻繁的建立、釋放連接所帶來的系統資源開銷。

第三、連接池的關閉。當應用程序退出時,關閉連接池中所有的連接,釋放連接池相關的資源,以便連接可以返回池中重復利用。我們可以通過Connection對象的Close或Dispose方法,也可以通過C#的using語句來關閉連接。該過程正好與創建相反。

*移除無效連接*

     無效連接,即不能正確連接到數據庫服務器的連接。對於連接池來說,存儲的與數據庫服務器的連接的數量是有限的。因此,對於無效連接,如果如不及時移除,將會浪費連接池的空間。其實你不用擔心,連接池管理器已經很好的為我們處理了這些問題。如果連接長時間空閑,或檢測到與服務器的連接已斷開,連接池管理器會將該連接從池中移除。

四,連接池的主要參數

最小連接數Min Pool Size: 默認為0。是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費.
最大連接數Max Pool Size: 默認為100。:是連接池能申請的最大連接數,如果數據庫連接請求超過次數,后面的數據庫連接請求將被加入到等待隊列中,這會影響以后的數據庫操作
最大空閑時間
獲取連接超時時間Connection Timeout:連接請求等待超時時間。默認為15秒,單位為秒。
超時重試連接次數

Pooling: 是否啟用連接池。ADO.NET默認是啟用連接池的,因此,你需要手動設置Pooling=false來禁用連接池。

這里放一個我們項目druid的配置

 

五,數據庫對比

第一、二代連接池

數據庫連接池 最新版本 發布時間
c3p0 c3p0-0.9.5.2 2015
dbcp 2.2.0 2017
druid 0.11.0 2017
HikariCP 2.7.6 2018

 

表格中可以看到,c3p0和dbcp等第一代基本已經死掉了, 二代產品對一代產品的超越是顛覆性的,除了一些“歷史原因”,你很難再找到第二條理由說服自己不選擇二代產品,但任何成功都不是偶然的,二代產品的成功很大程度上得益於前代產品們打下的基礎,站在巨人的肩膀上,新一代的連接池的設計師們將這一項“工具化”的產品,推向了極致。其中,最具代表性的兩款產品是:

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組件提供的,因為核心功能依賴於Pool,所以DBCP本身只能做小版本的更新,真正大版本的更迭則完全依托於pool。有很長一段時間,pool都還是停留在1.x版本,這直接導致DBCP也更新乏力。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

相較於其他產品,Druid另一個比較大的優勢,就是中文文檔比較全面(畢竟是國人的項目么),在github的wiki頁面,列舉了日常使用中可能遇到的問題,對一個新用戶來講,上面提供的內容已經足夠指導它完成產品的配置和使用了。

 現在項目開發中,我還是比較傾向於使用Durid,它不僅僅是一個數據庫連接池,它還包含一個ProxyDriver,一系列內置的JDBC組件庫,一個SQL Parser,所以我們項目目前用的也是這個連接池。

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集合了開源和商業數據庫連接池的優秀特性,並結合阿里巴巴大規模苛刻生產環境的使用經驗進行優化。

六,連接池需要注意的點 

1、並發問題
  為了使連接管理服務具有最大的通用性,必須考慮多線程環境,即並發問題。這個問題相對比較好解決,因為各個語言自身提供了對並發管理的支持像java,c#等等,使用synchronized(java)lock(C#)關鍵字即可確保線程是同步的。使用方法可以參考,相關文獻。

2、事務處理
  我們知道,事務具有原子性,此時要求對數據庫的操作符合“ALL-OR-NOTHING”原則,即對於一組SQL語句要么全做,要么全不做。 
  我們知道當2個線程共用一個連接Connection對象,而且各自都有自己的事務要處理時候,對於連接池是一個很頭疼的問題,因為即使Connection類提供了相應的事務支持,可是我們仍然不能確定那個數據庫操作是對應那個事務的,這是由於我們有2個線程都在進行事務操作而引起的。為此我們可以使用每一個事務獨占一個連接來實現,雖然這種方法有點浪費連接池資源但是可以大大降低事務管理的復雜性。

3、連接池的分配與釋放

  連接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,可以提高連接的復用度,從而降低建立新連接的開銷,同時還可以加快用戶的訪問速度。 
  對於連接的管理可使用一個List。即把已經創建的連接都放入List中去統一管理。每當用戶請求一個連接時,系統檢查這個List中有沒有可以分配的連接。如果有就把那個最合適的連接分配給他(如何能找到最合適的連接文章將在關鍵議題中指出);如果沒有就拋出一個異常給用戶,List中連接是否可以被分配由一個線程來專門管理捎后我會介紹這個線程的具體實現。

4、連接池的配置與維護
  連接池中到底應該放置多少連接,才能使系統的性能最佳?系統可采取設置最小連接數(minConnection)和最大連接數(maxConnection)等參數來控制連接池中的連接。比方說,最小連接數是系統啟動時連接池所創建的連接數。如果創建過多,則系統啟動就慢,但創建后系統的響應速度會很快;如果創建過少,則系統啟動的很快,響應起來卻慢。這樣,可以在開發時,設置較小的最小連接數,開發起來會快,而在系統實際使用時設置較大的,因為這樣對訪問客戶來說速度會快些。最大連接數是連接池中允許連接的最大數目,具體設置多少,要看系統的訪問量,可通過軟件需求上得到。 
  如何確保連接池中的最小連接數呢?有動態和靜態兩種策略。動態即每隔一定時間就對連接池進行檢測,如果發現連接數量小於最小連接數,則補充相應數量的新連接,以保證連接池的正常運轉。靜態是發現空閑連接不夠時再去檢查。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM