
一、背景
連接池簡介:
連接池初始化時創建一定數量的連接,然后從連接池中重用連接,而不是每次創建一個新的。
數據庫連接是一種關鍵的、有限的、昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤為突出。對數據庫連接的管理能顯著影響到整個應用程序的伸縮性和健壯性,影響到程序的性能指標。數據庫連接池正是針對這個問題提出來的。
數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有的數據庫連接,而不是再重新建立一個;釋放空閑時間超過最大空閑時間的數據庫連接來避免因為沒有釋放數據庫連接而引起的數據庫連接遺漏。這項技術能明顯提高對數據庫操作的性能。
什么是連接?
連接,是編程語言與數據庫交互的一種方式。
如下創建連接的代碼片段,將展示其內部創建原理:
String connUrl = "jdbc:mysql://your.database.domain/yourDBname"; Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection (connUrl);
當創建了一個Connection對象,它在內部的執行步驟:
- “DriverManager”檢查並注冊驅動程序,
- “com.mysql.jdbc.Driver”就是注冊的驅動程序,它會在驅動程序類中調用“connect(url…)”方法。
- com.mysql.jdbc.Driver的connect方法根據請求的“connUrl”,創建一個“Socket連接”,連接到IP為“your.database.domain”,默認端口3306的數據庫。
- 創建的Socket連接將被用來查詢指定的數據庫,並最終讓程序返回得到一個結果。
連接昂貴的體現?
如果創建Socket連接花費的時間比實際的執行查詢的操作所花費的時間還要更長。
因為連接資源數如果是1,它需要每次創建一個Socket連接來訪問DB。也就是說,如果在Web服務器上部署,每一個用戶訪問一次就連接到釋放,如果用戶數數以千計,那么這個過程也是數以千計的過程,時間和性能上消耗是非常巨大的。
因此,這就是昂貴的體現,將使用連接池把這些連接保存起來已供有需要的時候不用重新連接造成性能上的損失,從而提升性能。
二、影響因素
數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中,這些數據庫連接的數量是由最小數據庫連接數制約。無論這些數據庫連接是否被使用,連接池都將一直保證至少擁有這么多的連接數量。連接池的最大數據庫連接數量限定了這個連接池能占有的最大連接數,當應用程序向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中。要考慮到下列幾個因素:
1、最小連接數
是連接池一直保持的數據庫連接,所以如果應用程序對數據庫連接的使用量不大,將會有大量的數據庫連接資源被浪費。
2、最大連接數
是連接池能申請的最大連接數,如果數據庫連接請求超過此數,后面的數據庫連接請求將被加入到等待隊列中,這會影響之后的數據庫操作。
3、最小連接數與最大連接數差距
最小連接數與最大連接數相差太大,那么最先的連接請求將會獲利,之后超過最小連接數量的連接請求等價於建立一個新的數據庫連接。不過,這些大於最小連接數的數據庫連接在使用完不會馬上被釋放,它將被放到連接池中等待重復使用或是空閑超時后被釋放。
4、並發問題
為了使連接管理服務具有最大的通用性,必須考慮多線程環境,即並發問題。這個問題相對比較好解決,因為各個語言自身提供了對並發管理的支持像Java等等,使用synchronized(java)關鍵字即可確保線程是同步的。
5、事務處理
事務具有原子性,此時要求對數據庫的操作符合“ALL-OR-NOTHING”原則,即對於一組SQL語句要么全做,要么全不做。
當2個線程公用一個連接Connection對象,而且各自都有自己的事務要處理時候,對於連接池是一個很頭疼的問題,因為即使Connection類提供了相應的事務支持,可是仍然不能確定那個數據庫操作是對應那個事務的,這是由於有2個線程都在進行事務操作而引起的。為此可以使用每一個事務獨占一個連接來實現,雖然這種方法有點浪費連接池資源但是可以大大降低事務管理的復雜性。
6、連接池的分配與釋放
連接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,可以提高連接的復用度,從而降低建立新連接的開銷,同時還可以加快用戶的訪問速度。
對於連接的管理可使用一個List。即把已經創建的連接都放入List中去統一管理。每當用戶請求一個連接時,系統檢查這個List中有沒有可以分配的連接。如果有就把那個最合適的連接分配給它;如果沒有就拋出一個異常給用戶,List中連接是否可以被分配由一個線程來專門管理。
三、原理
連接池基本的思想是在系統初始化的時候,將數據庫連接作為對象存儲在內存中,當用戶需要訪問數據庫時,並非建立一個新的連接,而是從連接池中取出一個已建立的空閑連接對象。使用完畢后,用戶也並非將連接關閉,而是將連接放回連接池中,以供下一個請求訪問使用。而連接的建立、斷開都由連接池自身來管理。同時,還可以通過設置連接池的參數來控制連接池中的初始連接數、連接的上下限數以及每個連接的最大使用次數、最大空閑時間等等。也可以通過其自身的管理機制來監視數據庫連接的數量、使用情況等。
下面將展示實際代碼例子:
使用的連接池供應者,它的內部有一個連接池管理器,當它被初始化:
1、它創建連接池的默認大小,比如指定創建5個連接對象,並把它存放在“可用”狀態的任何集合或數組中。
... String connUrl = "jdbc:mysql://your.database.domain/yourDBname"; String driver = "com.mysql.jdbc.Driver"; private Map<java.sql.Connection, String> connectionPool = null; private void initPool() { try { connectionPool = new HashMap<java.sql.Connection, String>(); Class.forName(driver); java.sql.Connection con = DriverManager.getConnection(dbUrl); for (int poolInd = poolSize; poolInd < 0; poolInd++) { connectionPool.put(con, "AVAILABLE"); } } ...
2、當調用connectionProvider.getConnection(),然后它會從集合中獲取一個連接,當然狀態也會更改為“不可用”。
... public java.sql.Connection getConnection() throws ClassNotFoundException, SQLException { boolean isConnectionAvailable = true; for (Entry<java.sql.Connection, String> entry : connectionPool.entrySet()) { synchronized (entry) { if (entry.getValue()=="AVAILABLE") { entry.setValue("NOTAVAILABLE"); return (java.sql.Connection) entry.getKey(); } isConnectionAvailable = false; } } if (!isConnectionAvailable) { Class.forName(driver); java.sql.Connection con = DriverManager.getConnection(connUrl); connectionPool.put(con, "NOTAVAILABLE"); return con; } return null; } ...
3、當關閉得到的連接,ConnectionProvider是不會真正關閉連接。相反,只是將狀態更改為“AVAILABLE”。
... public void closeConnection(java.sql.Connection connection) throws ClassNotFoundException, SQLException { for (Entry<java.sql.Connection, String> entry : connectionPool.entrySet()) { synchronized (entry) { if (entry.getKey().equals(connection)) { //Getting Back the conncetion to Pool entry.setValue("AVAILABLE"); } } } } ...
基本上連接池的實際工作原理就是這樣,但也有可能使用不同的方式。
四、Java連接池選擇
在Java中開源的數據庫連接池有以下幾種 :
1、C3P0:是一個開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發布,包括了實現jdbc3和jdbc2擴展規范說明的Connection 和Statement 池的DataSources 對象。
2、Proxool:是一個Java SQL Driver驅動程序,提供了對選擇的其它類型的驅動程序的連接池封裝。可以非常簡單的移植到現存的代碼中,完全可配置,快速、成熟、健壯。可以透明地為現存的JDBC驅動程序增加連接池功能。
3、Jakarta DBCP:DBCP是一個依賴Jakartacommons-pool對象池機制的數據庫連接池。DBCP可以直接的在應用程序中使用。也許是年代的問題,也許這就是Tomat DBCP連接池,Tomcat默認使用的就是這個連接池。
4、DDConnectionBroker:是一個簡單、輕量級的數據庫連接池。
5、DBPool:是一個高效、易配置的數據庫連接池。它除了支持連接池應有的功能之外,還包括了一個對象池,使用戶能夠開發一個滿足自己需求的數據庫連接池。
6、XAPool:是一個XA數據庫連接池。它實現了javax.sql.XADataSource並提供了連接池工具。
7、Primrose:是一個Java開發的數據庫連接池。當前支持的容器包括Tomcat4&5、Resin3與JBoss3。它同樣也有一個獨立的版本,可以在應用程序中使用而不必運行在容器中。Primrose通過一個WEB接口來控制SQL處理的追蹤、配置,以及動態池管理。在重負荷的情況下可進行連接請求隊列處理。
8、SmartPool:是一個連接池組件,它模仿應用服務器對象池的特性。SmartPool能夠解決一些臨界問題如連接泄漏(connection leaks)、連接阻塞、打開的JDBC對象(如Statements、PreparedStatements)等。SmartPool的特性包括:
- 支持多個pool
- 自動關閉相關聯的JDBC對象
- 在所設定time-outs之后察覺連接泄漏
- 追蹤連接使用情況
- 強制啟用最近最少用到的連接
- 把SmartPool“包裝”成現存的一個pool
9、MiniConnectionPoolManager:是一個輕量級JDBC數據庫連接池。它只需要Java1.5(或更高)並且沒有依賴第三方包。
10、BoneCP:是一個快速、開源的數據庫連接池。幫用戶管理數據連接,讓應用程序能更快速地訪問數據庫。比C3P0/DBCP連接池速度快25倍。
11、Druid:Druid不僅是一個數據庫連接池,還包含一個ProxyDriver、一系列內置的JDBC組件庫、一個SQL Parser。支持所有JDBC兼容的數據庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等。
Druid針對Oracle和MySql做了特別優化,比如:
- Oracle的PS Cache內存占用優化
- MySql的ping檢測優化
Druid提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支持,這是一個手寫的高性能SQL Parser,支持Visitor模式,使得分析SQL的抽象語法樹很方便。
簡單SQL語句用時10微秒以內,復雜SQL用時30微秒。
通過Druid提供的SQL Parser可以在JDBC層攔截SQL做相應處理,比如說分庫分表、審計等。Druid防御SQL注入攻擊的WallFilter,就是通過Druid的SQL Parser分析語義實現的。
還有一個重要特定,這是阿里巴巴發布的。
五、dotnet連接池
1、ado.net默認已經開啟線程池,無需人工設置,但是現在市面上也有線程池的實現,不過非常少,也可以人工設置這個線程池的連接數等等。
參考:
http://blog.csdn.net/u012152619/article/details/46287419(詳細介紹,附帶配置)
http://www.oschina.net/question/157182_72094?from=20121007(詳細介紹)
http://blog.csdn.net/tuke_tuke/article/details/51532510(連接池實現)
http://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0(百度百科詳細介紹)
http://duzc2.iteye.com/blog/1536205(性能對比測試報告)
