# 資料庫連接池 ## **傳統獲取Connection問題分析** 1. 傳統的JDBC資料庫使用 DriverManager 來獲取, **每次向資料庫建立連接的時候都要將 Connection 載入到記憶體中,再驗證IP地址,用戶名和密碼(0.05 ~ 1 s 時間)**。需要資料庫連接的時候, ...
資料庫連接池
傳統獲取Connection問題分析
- 傳統的JDBC資料庫使用 DriverManager 來獲取, 每次向資料庫建立連接的時候都要將 Connection 載入到記憶體中,再驗證IP地址,用戶名和密碼(0.05 ~ 1 s 時間)。需要資料庫連接的時候,就向資料庫要求一個,頻繁的進行資料庫連接操作將占用很多的系統資源,容易造成伺服器崩潰。
- 每一次資料庫連接使用後都得斷開,如果程式出現異常而未能關閉,將導致資料庫記憶體泄漏,最終將導致重啟資料庫。
- 傳統獲取連接的方式,不能控制創建的連接數量,如果連接過多,也可能導致記憶體泄露,MySQL崩潰。
- 解決傳統開發中的資料庫連接問題,可以採用資料庫連接池技術。
案例:
package com.hspedu.jdbc.datasource;
import com.hspedu.jdbc.utils.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
public class ConQuestion {
//代碼 連接mysql 5000次
@Test
public void testCon(){
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++){
//使用傳統jdbc連接方式
Connection connection = JDBCUtils.getConnection();
//可能會拋出too many connections的異常
//再看看,如果得到連接就立即關閉,總共會耗時多久
JDBCUtils.close(null, null, connection);
}
long end = System.currentTimeMillis();
System.out.println("耗時 = " + (end - start));//耗時 = 6598
}
}
資料庫連接池原理
-
基本介紹
- 預先在緩衝池中放入一定數量的連接,當需要建立資料庫連接時,只需要從“緩衝池”中取出一個,使用完畢之後再放回去(放回連接,其實就是指程式不再引用這個連接)。
- 資料庫連接池負責分配、管理和釋放資料庫連接,它允許應用程式重覆使用一個現有的資料庫連接,而不是重新建立一個。
- 當應用程式向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待隊列中。
原理示意圖:
常用的資料庫連接池
JDBC 的資料庫連接池使用 javax.sql.DateSource 來表示,DataSource 只是一個介面,該介面通常由第三方提供實現。
資料庫連接池種類:**
- C3P0 資料庫連接池:速度相對較慢,穩定性不錯 (hibernate,spring);
- DBCP 資料庫連接池:速度相對C3P0較快,但不穩定;
- Proxool 資料庫連接池:有監控連接池狀態的功能,穩定性較C3P0差一點;
- BoneCP 資料庫連接池:速度快;
- Druid(德魯伊) 資料庫連接池:是阿裡提供的資料庫連接池,集DBCP、C3P0、Proxool優點於一身的資料庫連接池;
C3P0的使用
需要自行導入相關的jar包。
- 方式1:相關參數,在程式中指定user,url, password。
- 方式2: 使用配置文件的模板來完成。
代碼演示:
package com.hspedu.jdbc.datasource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Test;
import java.beans.PropertyVetoException;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class C3P0_ {
//方式1:相關參數,在程式中指定user, url, password
@Test
public void testC3P0_01() throws IOException, PropertyVetoException, SQLException {
//1. 創建一個數據源對象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2. 通過配置文件獲取相關連接信息
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//給數據源(數據源負責連接,連接池) comboPooledDataSource 設置參數
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
//設置初始化連接數
comboPooledDataSource.setInitialPoolSize(10);
//最大連接數
comboPooledDataSource.setMaxPoolSize(50);
//獲取連接
//測試連接池的效率,連接5000次
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = comboPooledDataSource.getConnection();//這個方法就是從DateSource介面實現的
// System.out.println("連接OK");
//關閉
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000次連接mysql 耗時 = " + (end - start));//c3p0 5000次連接mysql 耗時 = 280,之前傳統的耗時 = 6598
}
//第二種方式, 使用配置文件的模板來完成
//1. 將c3p0 提供的 c3p0.config.xml 拷貝到src目錄
//2. 該文件指定了連接資料庫和連接池的相關參數
@Test
public void testC3P0_02() throws SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_edu");//填入數據源名稱
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Connection connection = comboPooledDataSource.getConnection();//這個方法就是從DateSource介面實現的
// System.out.println("連接OK");
//關閉
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 第二種方式500000次連接mysql 耗時 = " + (end - start));//c3p0 第二種方式500000次連接mysql 耗時 = 1452
}
}
配置文件 c3p0-config.xml:
<c3p0-config>
<named-config name="hsp_edu">
<!-- 連接參數 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_learning</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 連接池參數 -->
<!-- 每次增長的連接數 -->
<property name="acquireIncrement">5</property>
<!-- 初始的連接數 -->
<property name="initialPoolSize">5</property>
<!-- 最大連接數 -->
<property name="maxPoolSize">10</property>
<!-- 最小連接數 -->
<property name="minPoolSize">5</property>
<!-- 每個連接對象可連接的最多的命令對象數 -->
<property name="maxStatementPerConnection">2</property>
</named-config>
</c3p0-config>
Druid(德魯伊)的使用
需要自行導入相關的jar包。
代碼演示:
package com.hspedu.jdbc.datasource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.jupiter.api.Test;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
public class Druid_ {
@Test
public void testDruid() throws Exception {
//1. 加入 Druid jar包
//2. 加入 配置文件 druid.properties,將該文件拷貝到項目的src目錄
//3. 創建 Properties 對象
Properties properties = new Properties();
properties.load(new FileInputStream("src\\druid.properties"));
//4. 創建一個指定參數的資料庫連接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Connection connection = dataSource.getConnection();
// System.out.println("連接成功");
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("druid連接池, 連接500000次 耗時 = " + (end - start));//druid連接池, 連接500000次 耗時 = 549
}
}
配置文件 druid.properties:
#key=value
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_learning?rewriteBatchedStatements=true
username=root
password=root
#initial connection size
initialSize=10
#min idle connection size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000