1、爲何須要鏈接池java
數據庫鏈接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網頁應用程序中體現得尤其突出。 一個數據庫鏈接對象均對應一個物理數據庫鏈接,每次操做都打開一個物理鏈接,使用完都關閉鏈接,這樣形成系統的 性能低下。
數據庫鏈接池的解決方案是在應用程序啓動時創建足夠的數據庫鏈接,並講這些鏈接組成一個鏈接池,由應用程序動態地對池中的鏈接進行申請、使用和釋放。
對於多於鏈接池中鏈接數的併發請求,應該在請求隊列中排隊等待。而且應用程序能夠根據池中鏈接的使用率,動態增長或減小池中的鏈接數。
鏈接池技術儘量多地重用了消耗內存地資源,大大節省了內存,提升了服務器地服務效率,可以支持更多的客戶服務。經過使用鏈接池,將大大提升程序運行效率,同時,咱們能夠經過其自身的管理機制來監視數據庫鏈接的數量、使用狀況等。mysql
2、定義sql
數據庫鏈接池負責分配、管理和釋放數據庫鏈接,它容許應用程序重複使用一個現有的數據庫鏈接,而不是再從新創建一個。數據庫
3、工做原理apache
鏈接池的工做原理主要由三部分組成,分別爲鏈接池的創建、鏈接池中鏈接的使用管理、鏈接池的關閉。
第1、鏈接池的創建。通常在系統初始化時,鏈接池會根據系統配置創建,並在池中建立了幾個鏈接對象,以便使用時能從鏈接池中獲取。鏈接池中的鏈接不能隨意建立和關閉,這樣避免了鏈接隨意創建和關閉形成的系統開銷。Java中提供了不少容器類能夠方便的構建鏈接池,例如Vector、Stack等。
第2、鏈接池的管理。鏈接池管理策略是鏈接池機制的核心,鏈接池內鏈接的分配和釋放對系統的性能有很大的影響。其管理策略是: 當客戶請求數據庫鏈接時,首先查看鏈接池中是否有空閒鏈接,若是存在空閒鏈接,則將鏈接分配給客戶使用;若是沒有空閒鏈接,則查看當前所開的鏈接數是否已經達到最大鏈接數,若是沒達到就從新建立一個鏈接給請求的客戶;若是達到就按設定的最大等待時間進行等待,若是超出最大等待時間,則拋出異常給客戶。
當客戶釋放數據庫鏈接時,先判斷該鏈接的引用次數是否超過了規定值,若是超過就從鏈接池中刪除該鏈接,不然保留爲其餘客戶服務。
該策略保證了數據庫鏈接的有效複用,避免頻繁的創建、釋放鏈接所帶來的系統資源開銷。
第3、鏈接池的關閉。當應用程序退出時,關閉鏈接池中全部的鏈接,釋放鏈接池相關的資源,該過程正好與建立相反。tomcat
4、傳統數據庫鏈接對比數據庫鏈接池服務器
不使用數據庫鏈接池的步驟:
TCP創建鏈接的三次握手
MySQL認證的三次握手
真正的SQL執行
MySQL的關閉
TCP的四次握手關閉
能夠看到,爲了執行一條SQL,卻多了很是多咱們不關心的網絡交互。網絡優勢:實現簡單
缺點:併發
- 網絡IO較多
- 數據庫的負載較高
- 響應時間較長及QPS較低
- 應用頻繁的建立鏈接和關閉鏈接,致使臨時對象較多,GC頻繁
- 在關閉鏈接後,會出現大量TIME_WAIT 的TCP狀態(在2個MSL以後關閉)
使用數據庫鏈接池的步驟:框架
第一次訪問的時候,須要創建鏈接。 可是以後的訪問,均會複用以前建立的鏈接,直接執行SQL語句。
優勢:
較少了網絡開銷
系統的性能會有一個實質的提高
沒了麻煩的TIME_WAIT狀態
5、經常使用鏈接池對比
目前存在多個開源的java數據庫鏈接池,這些鏈接池都是在java.sql基礎上編寫而成。
建數據庫test,而後在建立表
CREATE TABLE `user` ( `userid` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(255) DEFAULT NULL, `password` VARCHAR(255) DEFAULT NULL, `email` VARCHAR(255) DEFAULT NULL, `phone` VARCHAR(255) DEFAULT NULL, `status` VARCHAR(255) NOT NULL DEFAULT '0', `code` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`userid`) ) ENGINE=INNODB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;
DBCP鏈接池
DBCP 是 Apache 軟件基金組織下的開源鏈接池實現,使用DBCP數據源,應用程序應在系統中增長以下兩個 jar 文件:
Commons-dbcp.jar:鏈接池的實現
Commons-pool.jar:鏈接池實現的依賴庫
Tomcat 的鏈接池正是採用該鏈接池來實現的。該數據庫鏈接池既能夠與應用服務器整合使用,也可由應用程序獨立使用。
核心類:org.apache.commons.dbcp2.BasicDataSource extends Object implements DataSource
import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import javax.sql.DataSource; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; import org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory; import org.junit.Test; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class SqlDriverManage { private final String driverClassName = "com.mysql.cj.jdbc.Driver"; private final String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC"; private final String userName = "root"; private final String userPassword = "123456"; Connection con=null; PreparedStatement pst=null; ResultSet rs=null; /* * 使用java.sql.DriverManager類----傳統方式,沒有使用鏈接池,須要引入mysql驅動包 */ @Test public void test1(){ try { //1.驅動註冊程序 Class.forName(driverClassName); //2.獲取鏈接對象 con= DriverManager.getConnection(url,userName, userPassword); //3.準備sql String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (Exception e) { e.printStackTrace(); }finally{ //7.關閉鏈接(順序:後打開的先關閉) if(rs!=null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(pst!=null){ try { pst.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(con!=null){ try { con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
/* * 第一種方式使用實現了javax.sql.DataSource接口的子類--DBCP鏈接池硬編碼 * 在上述代碼中添加單元測試代碼 */ @Test public void test2(){ try { //1.驅動註冊程序 BasicDataSource bds=new BasicDataSource(); bds.setDriverClassName(driverClassName); bds.setUrl(url); bds.setUsername(userName); bds.setPassword(userPassword); //2.獲取鏈接對象 con=bds.getConnection(); //3.準備sql語句 String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
/* *第二種方式使用實現了javax.sql.DataSource接口的子類--DBCP鏈接池軟編碼 */ @Test public void test3(){ try { //1.驅動註冊程序 Properties pro=new Properties(); BasicDataSource bds=null; InputStream inStream=SqlDriverManage.class.getResourceAsStream("dbcp.properties"); pro.load(inStream); bds=new BasicDataSourceFactory().createDataSource(pro); //2.獲取鏈接對象 con=bds.getConnection(); //3.準備sql語句 String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
#dbcp.properties driverClassName=com.mysql.cj.jdbc.Driver username=root password=123456 url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
在此過程當中出現的問題
解決方法 添加tomcat斌目錄下的tomcat-juli.jar
解決方法:
- DBCP鏈接池配置文件中的key與BasicDataSouce中的屬性同樣
- URl後追加allowPublicKeyRetrieval=true
C3P0鏈接池:
最經常使用的鏈接池技術!Spring框架,默認支持C3P0鏈接池技術!
須要引入c3p0-0.9.1.2.jar
核心類:com.mchange.v2.c3p0.ComboPooledDataSource implements PooledDataSource extends DataSource
/* * 使用實現了javax.sql.DataSource接口的子類--c3p0鏈接池硬編碼 */ @Test public void test4(){ try { //1.驅動註冊程序 ComboPooledDataSource cpd=new ComboPooledDataSource(); cpd.setDriverClass(driverClassName); cpd.setJdbcUrl(url); cpd.setUser(userName); cpd.setPassword(userPassword); //2.獲取鏈接對象 con=cpd.getConnection(); //3.準備sql語句 String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
/* * 使用實現了javax.sql.DataSource接口的子類--c3p0鏈接池配置文件xml */ @Test public void test5(){ try { //1.驅動註冊程序 ComboPooledDataSource cpd=new ComboPooledDataSource("mysql-config"); //ComboPooledDataSource cpd=new ComboPooledDataSource(); //2.獲取鏈接對象 con=cpd.getConnection(); //3.準備sql語句 String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
<!--自動加載src下c3p0的配置文件【c3p0-config.xml】,必須是放在src下。--> <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC</property> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="user">root</property> <property name="password">123456</property> </default-config> <named-config name="mysql-config"> <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC</property> <property name="driverClass">com.mysql.cj.jdbc.Driver</property> <property name="user">root</property> <property name="password">123456</property> </named-config> </c3p0-config>
在此過程當中出現的問題
DRUID簡介
Druid是阿里巴巴開發的號稱爲監控而生的數據庫鏈接池。能夠監控數據庫訪問性能,Druid內置提供了一個功能強大的StatView插件,可以詳細統計SQL的執行性能,這對於線上分析數據庫訪問性能有幫助。
核心類 com.alibaba.druid.pool.DruidDataSource extends DruidAbstractDataSource implements DataSource
/* * 使用實現了javax.sql.DataSource接口的子類--druid鏈接池硬編碼 */ @Test public void test6(){ try { //1.驅動註冊程序 DruidDataSource dds=new DruidDataSource(); dds.setDriverClassName(driverClassName); dds.setUrl(url); dds.setUsername(userName); dds.setPassword(userPassword); //2.獲取鏈接對象 con=dds.getConnection(); //3.準備sql語句 String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
/* * 使用實現了javax.sql.DataSource接口的子類--druid鏈接池軟編碼 */ @Test public void test7(){ try { //1.驅動註冊程序 Properties pro=new Properties(); DataSource dds=null; InputStream inStream=SqlDriverManage.class.getResourceAsStream("druid.properties"); pro.load(inStream); dds=com.alibaba.druid.pool.DruidDataSourceFactory.createDataSource(pro); //2.獲取鏈接對象 con=dds.getConnection(); //3.準備sql語句 String sql="select * from user"; //4.建立prepareStatement pst=con.prepareStatement(sql); //5.執行sql語句,獲得返回結果 rs= pst.executeQuery(); //6.遍歷結果,索引從1開始 while(rs.next()){ System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
#druid.properties driverClassName=com.mysql.cj.jdbc.Driver username=root password=123456 url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=truecharacterEncoding=UTF-8&useSSL=false&serverTimezone=UTC