Java連線池
轉載自(43條訊息) Java連線池詳解_方全的專欄-CSDN部落格_java連線池
對於共享資源,有一個很著名的設計模式:資源池(Resource Pool)。該模式正是為了解決資源的頻繁分配﹑釋放所造成的問題。為解決我們的問題,可以採用資料庫連線池技術。資料庫連線池的基本思想就是為資料庫連線建立一個“緩衝池”。預先在緩衝池中放入一定數量的連線,當需要建立資料庫連線時,只需從“緩衝池”中取出一個,使用完畢之後再放回去。我們可以通過設定連線池最大連線數來防止系統無盡的與資料庫連線。更為重要的是我們可以通過連線池的管理機制監視資料庫的連線的數量﹑使用情況,為系統開發﹑測試及效能調整提供依據。
為什麼使用連線池
String connUrl ="jdbc:mysql://your.database.domain/yourDBname"; Class.forName("com.mysql.jdbc.Driver"); Connection con =DriverManager.getConnection (connUrl);
當我們建立了一個Connection物件,它在內部都執行了什麼:
1.“DriverManager”檢查並註冊驅動程式;
2.“com.mysql.jdbc.Driver”就是我們註冊了的驅動程式,它會在驅動程式類中呼叫“connect(url…)”方法。
3.com.mysql.jdbc.Driver的connect方法根據我們請求的“connUrl”,建立一個“Socket連線”,連線到IP為“your.database.domain”,預設埠3306的資料庫。
4.建立的Socket連線將被用來查詢我們指定的資料庫,並最終讓程式返回得到一個結果。
簡單的獲取一個連線,系統卻要在背後做很多消耗資源的事情,大多時候,建立連線的時間比執行sql語句的時間還要長。
使用者每次請求都需要向資料庫獲得連結,而資料庫建立連線通常需要消耗相對較大的資源,建立時間也較長。假設網站一天10萬訪問量,資料庫伺服器就需要建立10萬次連線,極大的浪費資料庫的資源,並且極易造成資料庫伺服器記憶體溢位、拓機。
採用連線池技術後的過程如下:
資料庫連線是一種關鍵的有限的昂貴的資源,這一點在多使用者的網頁應用程式中體現的尤為突出。對資料庫連線的管理能顯著影響到整個應用程式的伸縮性和健壯性,影響到程式的效能指標。資料庫連線池負責分配,管理和釋放資料庫連線,它允許應用程式重複使用一個現有的資料庫連線,而不是重新建立一個。
需要注意的問題
1、併發問題
為了使連線管理服務具有最大的通用性,必須考慮多執行緒環境,即併發問題。這個問題相對比較好解決,因為各個語言自身提供了對併發管理的支援像java,c#等等,使用synchronized(java)、lock(C#)關鍵字即可確保執行緒是同步的。
2、事務處理
我們知道,事務具有原子性,此時要求對資料庫的操作符合“ALL-OR-NOTHING”原則,即對於一組SQL語句要麼全做,要麼全不做。
我們知道當2個執行緒公用一個連線Connection物件,而且各自都有自己的事務要處理時候,對於連線池是一個很頭疼的問題,因為即使Connection類提供了相應的事務支援,可是我們仍然不能確定哪個資料庫操作是對應哪個事務的,這是由於我們有2個執行緒都在進行事務操作而引起的。為此我們可以使用每一個事務獨佔一個連線來實現,雖然這種方法有點浪費連線池資源但是可以大大降低事務管理的複雜性。
3、連線池的分配與釋放
連線池的分配與釋放,對系統的效能有很大的影響。合理的分配與釋放,可以提高連線的複用度,從而降低建立新連線的開銷,同時還可以加快使用者的訪問速度。
對於連線的管理可使用一個List。即把已經建立的連線都放入List中去統一管理。每當使用者請求一個連線時,系統檢查這個List中有沒有可以分配的連線。如果有就把那個最合適的連線分配給他(如何能找到最合適的連線文章將在關鍵議題中指出);如果沒有就丟擲一個異常給使用者,List中連線是否可以被分配由一個執行緒來專門管理。
4、連線池的配置與維護
連線池中到底應該放置多少連線,才能使系統的效能最佳?系統可採取設定最小連線數(minConnection)和最大連線數(maxConnection)等引數來控制連線池中的連線。比方說,最小連線數是系統啟動時連線池所建立的連線數。如果建立過多,則系統啟動就慢,但建立後系統的響應速度會很快;如果建立過少,則系統啟動的很快,響應起來卻慢。這樣,可以在開發時,設定較小的最小連線數,開發起來會快,而在系統實際使用時設定較大的,因為這樣對訪問客戶來說速度會快些。最大連線數是連線池中允許連線的最大數目,具體設定多少,要看系統的訪問量,可通過軟體需求上得到。
如何確保連線池中的最小連線數呢?有動態和靜態兩種策略。動態即每隔一定時間就對連線池進行檢測,如果發現連線數量小於最小連線數,則補充相應數量的新連線,以保證連線池的正常運轉。靜態是發現空閒連線不夠時再去檢查。
Tomcat連線池
Tomcat預設使用的是DBCP資料庫連線池,其實從本質上講,Tomcat是利用Apache Commons DBCP來實現的,只不過把特定的功能整合到了tomcat-dbcp.jar包中。
使用法法如下:
步驟1:
在Tomcat中Context.xml中新增
<!-- path表示站點的訪問方式 --> <!-- 例:http://localhost:8080/test 配置為/test --> <!-- docBase="fileLocation" 應用儲存的實際路徑,沒有的話則從webapps目錄找 --> <!-- Context標籤內的這些屬性都可以省略不寫,使用預設的設定 --> <Context path="/TomcatDbPools" docBase="TomcatDbPools" debug="0" reloadable="true"> <!-- 使用DBCP配置的資料來源 --> <Resource <!-- 指定資源池的Resource的JNDI的名字,就是給連線池起的名字 --> name="jdbc/mysql_connect" <!-- 管理許可權,指定管理Resource的Manager,可以是Container或Application --> auth="Container" <!--指出Resource所屬的類名,是什麼型別的資料來源--> type="javax.sql.DataSource" <!-- 資料庫驅動類 --> driverClassName="com.mysql.jdbc.Driver" <!-- 資料庫連線url--> url=" jdbc:mysql://localhost:3306/test" <!-- 資料庫使用者名稱 --> username="admin" <!-- 資料庫密碼 --> password="123456" <!-- 連線池最大啟用的連線數,設為0表示無限制--> maxActive="100" <!-- 連線池中最多可空閒的連線數 --> maxIdle="30" <!-- 為連線最大的等待時間,單位毫秒,如果超過此時間將接到異常。設為-1表示無限制--> maxWait="10000" /> </context>
注:還可以用minIdle配置連線池中最少空閒maxIdle個連線,用initialSize配置初始化連線數目。可同時配置多個數據源。
如果在Tomcat的server.xml檔案中配置資料來源,有兩種方法都可以實現:
方法1:將上面的配置內容直接新增在<Host>節點下。
方法2:在<GlobalNamingResources>節點下新增:
<GlobalNamingResources> <!-- 這裡的factory指的是該Resource 配置使用的是哪個資料來源配置類,這裡使用的是tomcat自帶的標準資料來源Resource配置類,--> <!-- 這個類也可以自己寫,實現javax.naming.spi.ObjectFactory 介面即可。 --> <!-- 某些地方使用的commons-dbcp.jar中的org.apache.commons.dbcp.BasicDataSourceFactory,--> <!-- 如果使用這個就需把commons-dbcp.jar及其依賴的jar包,都放在tomcat的lib下,光放在工程的WEB-INF/lib下是不夠的。 --> <Resource name="mysql_connect" factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory" maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/TomcatDbPool1" password="123456" type="javax.sql.DataSource" url="jdbc:mysql://localhost:3306/test" username="root"/> </GlobalNamingResources>
然後在context.xml檔案中的<Context></Context>節點中加入如下內容:
<ResourceLink name="jdbc/mysql_connect" global="mysql_connect" type="javax.sql.DataSource"/>
在server.xml中配置的資料來源是全域性的,所有專案都可以使用。全域性的resource只是為了重用,方便所有該tomcat下的web工程的資料來源管理,但如果你的tomcat不會同時載入多個web工程,也就是說一個tomcat只加載一個web工程時,是沒有必要配置全域性的resource的。
此外,還需要將mysql的Java驅動類以及其他依賴包(如果有)放到tomcat的lib目錄下。
步驟2:
在web.xml中,配置<resource-ref>元素以在web應用中引用JNDI資源。
<resource-ref> <!-- 對該資源的描述語言 --> <description> dbcpconnect</description> <!-- 引用的資源名,必須與Context.xml中的名字一致 --> <res-ref-name> jdbc/mysql_connect </res-ref-name> <!-- 資源型別 --> <res-type>javax.sql.DataSource</res-type> <!-- 管理許可權 --> <res-auth>Container</res-auth> </resource-ref>
步驟3:
在Web應用中使用資料來源
//獲得對資料來源的引用:
Context ctx =new InitialContext();
//java:comp/env/是java中JNDI固定寫法。
DataSource ds =(DataSource) ctx.lookup("java:comp/env/jdbc/mysql_connect ");
//獲得資料庫連線物件:
Connection conn= ds.getConnection();
//返回資料庫連線到連線池:
conn.close();
DBCP連線池
DBCP 是 Apache 軟體基金組織下的開源連線池實現,要使用DBCP資料來源,需要應用程式應在系統中增加如下兩個 jar 檔案:
Commons-dbcp.jar:連線池的實現
Commons-pool.jar:連線池實現的依賴庫
Tomcat 的連線池正是採用該連線池來實現的。該資料庫連線池既可以與應用伺服器整合使用,也可由應用程式獨立使用。
步驟1:
在類目錄下加入dbcp的配置檔案:dbcp.properties
#資料庫驅動 driverClassName=com.mysql.jdbc.Driver #資料庫連線地址 url=jdbc:mysql://localhost/test #使用者名稱 username=root #密碼 password=123456 #連線池的最大資料庫連線數。設為0表示無限制 maxActive=30 #最大空閒數,資料庫連線的最大空閒時間。超過空閒時間,資料庫連 #接將被標記為不可用,然後被釋放。設為0表示無限制 maxIdle=10 #最大建立連線等待時間。如果超過此時間將接到異常。設為-1表示無限制 maxWait=1000 #超過removeAbandonedTimeout時間後,是否進行沒用連線(廢棄)的回收(預設為false,調整為true) removeAbandoned=true #超過時間限制,回收沒有用(廢棄)的連線(預設為 300秒) removeAbandonedTimeout=180
步驟2:
在獲取資料庫連線的工具類(如jdbcUtils)的靜態程式碼塊中建立池:
import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSourceFactory; /** * 在java中,編寫資料庫連線池需實現java.sql.DataSource介面,每一種資料庫連線池都是DataSource介面的實現 * DBCP連線池就是java.sql.DataSource介面的一個具體實現 */ public classJdbcUtils_DBCP { private static DataSource ds = null; //在靜態程式碼塊中建立資料庫連線池 static{ try{ //載入dbcp.properties配置檔案 InputStream in =JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp.properties"); Properties prop = new Properties(); prop.load(in); //建立資料來源 ds =BasicDataSourceFactory.createDataSource(prop); }catch (Exception e) { throw newExceptionInInitializerError(e); } } //從資料來源中獲取資料庫連線 public static Connection getConnection()throws SQLException{ //從資料來源中獲取資料庫連線 return ds.getConnection(); } //釋放連線 public static void release(Connection conn){ if(conn!=null){ try{ //將Connection連線物件還給資料庫連線池 conn.close(); }catch (Exception e) { e.printStackTrace(); } } } }
步驟3:
在應用中獲取連線
Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try{ //獲取資料庫連線 conn =JdbcUtils_DBCP.getConnection(); …… }catch (Exception e) { e.printStackTrace(); }finally{ //釋放資源 JdbcUtils_DBCP.release(conn); }
C3P0連線池
c3p0是一個開源的JDBC連線池,它實現了資料來源和JNDI繫結,支援JDBC3規範和JDBC2的標準擴充套件。c3p0一般是與Hibernate,Spring等框架一塊使用的,當然也可以單獨使用。
dbcp沒有自動回收空閒連線的功能,c3p0有自動回收空閒連線功能。
使用c3p0需要匯入c3p0.jar、mchange-commons-.jar,如果操作的是Oracle資料庫,那麼還需要匯入c3p0-oracle-thin-extras-pre1.jar。
步驟1:
在類目錄下加入C3P0的配置檔案:c3p0-config.xml
<c3p0-config> <!-- C3P0的預設(預設)配置,--> <!-- 如果在程式碼中“ComboPooledDataSourceds = new ComboPooledDataSource();”這樣寫就表示使用的是C3P0的預設(預設)配置資訊來建立資料來源 --> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/anysearch</property> <property name="user">root</property> <property name="password">123456</property> <!--當連線池中的連線耗盡的時候c3p0一次同時獲取的連線數。Default:3 --> <property name="acquireIncrement">5</property> <!--初始化的連線數,取值應在minPoolSize與maxPoolSize之間。Default: 3--> <property name="initialPoolSize">10</property> <!--連線池中保留的最小連線數--> <property name="minPoolSize">5</property> <!--連線池中保留的最大連線數。Default:15 --> <property name="maxPoolSize">20</property> <!--定義在從資料庫獲取新連線失敗後重復嘗試的次數。Default: 30 --> <property name="acquireRetryAttempts">30</property> <!--兩次連線中間隔時間,單位毫秒。Default: 1000 --> <property name="acquireRetryDelay">1000</property> <!--連線關閉時預設將所有未提交的操作回滾。Default: false --> <property name="autoCommitOnClose">false</property> </default-config> <!-- C3P0的命名配置,--> <!-- 如果在程式碼中“ComboPooledDataSourceds = new ComboPooledDataSource("MySQL");”這樣寫就表示使用的是name是MySQL的配置資訊來建立資料來源 --> <named-config name="MySQL"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/test2</property> <property name="user">root</property> <property name="password">123456</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> </named-config> </c3p0-config>
還有更多可設定的引數,具體可查閱相關資料。
步驟2:
在獲取資料庫連線的工具類(如jdbcUtils)的靜態程式碼塊中建立池
import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JdbcUtils_C3P0 { private static ComboPooledDataSource ds =null; //在靜態程式碼塊中建立資料庫連線池 static{ try{ //通過程式碼建立C3P0資料庫連線池 /*ds = new ComboPooledDataSource(); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/test"); ds.setUser("root"); ds.setPassword("123456"); ds.setInitialPoolSize(10); ds.setMinPoolSize(5); ds.setMaxPoolSize(20);*/ //通過讀取C3P0的xml配置檔案建立資料來源,C3P0的xml配置檔案c3p0-config.xml必須放在src目錄下 //ds = newComboPooledDataSource();//使用C3P0的預設配置來建立資料來源 ds = newComboPooledDataSource("MySQL");//使用C3P0的命名配置來建立資料來源 }catch (Exception e) { throw newExceptionInInitializerError(e); } } //從資料來源中獲取資料庫連線 public static Connection getConnection()throws SQLException{ //從資料來源中獲取資料庫連線 return ds.getConnection(); } //釋放連結 public static void release(Connection conn){ if(conn!=null){ try{ //將Connection連線物件還給資料庫連線池 conn.close(); }catch (Exception e) { e.printStackTrace(); } } } }
步驟3:
在應用中獲取連線
Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try{ //獲取資料庫連線 conn = JdbcUtils_C3P0.getConnection(); …… }catch (Exception e) { e.printStackTrace(); }finally{ //釋放資源 JdbcUtils_C3P0release(conn); }
其他連線池
此外,還有其他的連線池可供選擇,比如使用比較廣泛的Proxool。Proxool是一種Java資料庫連線池技術。是sourceforge下的一個開源專案,這個專案提供一個健壯、易用的連線池,最為關鍵的是這個連線池提供監控的功能,方便易用,便於發現連線洩漏的情況。
proxool和 c3p0能夠更好的支援高併發,但是在穩定性方面略遜於dpcp。
可根據專案的實際需要來選擇連線池。