拓展閱讀 第一節 從零開始手寫 mybatis(一)MVP 版本。 第二節 從零開始手寫 mybatis(二)mybatis interceptor 插件機制詳解 第三節 從零開始手寫 mybatis(三)jdbc pool 從零實現資料庫連接池 第四節 從零開始手寫 mybatis(四)- myb ...
拓展閱讀
第二節 從零開始手寫 mybatis(二)mybatis interceptor 插件機制詳解
第三節 從零開始手寫 mybatis(三)jdbc pool 從零實現資料庫連接池
第四節 從零開始手寫 mybatis(四)- mybatis 事務管理機制詳解
連接池的作用
資源重用
由於資料庫連接得到重用,避免了頻繁創建、釋放連接引起的大量性能開銷。在減少系統消耗的基礎上,
另一方面也增進了系統運行環境的平穩性(減少記憶體碎片以及資料庫臨時進程/線程的數量)。
更快的系統響應速度
資料庫連接池在初始化過程中,往往已經創建了若幹資料庫連接置於池中備用。此時連接的初始化工作均已完成。
對於業務請求處理而言,直接利用現有可用連接,避免了資料庫連接初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間。
新的資源分配手段
對於多應用共用同一資料庫的系統而言,可在應用層通過資料庫連接的配置,使用資料庫連接池技術。
設置某一應用最大可用資料庫連接數,避免某一應用獨占所有資料庫資源。
統一的連接管理,避免資料庫連接泄漏
在較為完備的資料庫連接池實現中,可根據預先設定的連接占用超時時間,強制收回被超時占用的連接。
從而避免了常規資料庫連接操作中可能出現的資源泄漏(當程式存在缺陷時,申請的連接忘記關閉,這時候,就存在連接泄漏了)。
中間件
常見實現對比
參考網上資料Druid > TomcatJDBC > DBCP > C3P0,BoneCP 的性能方面沒有深入比較,應該和 Tomcat Jdbc 差不多。
對於小型的系統,併發壓力不大時,選擇哪一種資料庫連接池差別不會很大,主要考慮的應該是連接池的穩定性。
當併發量較高時,一般不會選擇使用 DBCP 和C3P0,選 Druid 是較好的。
手動實現
自己實現一個簡化版,便於理解原理。
- 連接池介面
public interface IPool {
/**
* 獲取新的資料庫鏈接
* @return 資料庫鏈接
*/
PoolConnection getPoolConnection();
}
其中 PoolConnection 如下:
public class PoolConnection {
/**
* 是否繁忙
*/
private volatile boolean isBusy;
/**
* 資料庫鏈接信息
*/
private Connection connection;
}
- 核心實現
public class PoolImpl implements IPool {
/**
* 資料庫驅動
*/
private final String jdbcDriver;
/**
* 資料庫連接
*/
private final String jdbcUrl;
/**
* 資料庫用戶名
*/
private final String username;
/**
* 資料庫密碼
*/
private final String passowrd;
/**
* 連接池大小
*/
private final int size;
/**
* 資料庫連接池列表
*/
private List<PoolConnection> poolConnections = new ArrayList<>();
public PoolImpl(String jdbcDriver, String jdbcUrl, String username, String passowrd, int size) {
this.jdbcDriver = jdbcDriver;
this.jdbcUrl = jdbcUrl;
this.username = username;
this.passowrd = passowrd;
this.size = size;
init();
}
private void init() {
try {
//1. 註冊資料庫連接信息
Driver sqlDriver = (Driver) Class.forName(jdbcDriver).newInstance();
DriverManager.registerDriver(sqlDriver);
//2. 初始化連接池
initConnectionPool();
} catch (InstantiationException | IllegalAccessException | SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 初始化鏈接
* @throws SQLException sql 異常
*/
private void initConnectionPool() throws SQLException {
for(int i = 0; i < size; i++) {
Connection connection = DriverManager.getConnection(jdbcUrl, username, passowrd);
PoolConnection poolConnection = new PoolConnection(false, connection);
poolConnections.add(poolConnection);
}
}
@Override
public PoolConnection getPoolConnection() {
if(poolConnections.size() <= 0) {
return null;
}
PoolConnection poolConnection = getRealConnection();
while (poolConnection == null) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
poolConnection = getRealConnection();
}
return poolConnection;
}
/**
* 獲取資料庫鏈接對象
* @return 資料庫鏈接對象
*/
private synchronized PoolConnection getRealConnection() {
for(PoolConnection poolConnection : poolConnections) {
// 尋找不處於繁忙狀態的連接
if(!poolConnection.isBusy()) {
Connection connection = poolConnection.getConnection();
// 測試當前連接是否有效
try {
if(!connection.isValid(5000)) {
Connection validConnection = DriverManager.getConnection(jdbcUrl, username, passowrd);
poolConnection.setConnection(validConnection);
}
} catch (SQLException e) {
e.printStackTrace();
}
// 設置為繁忙
poolConnection.setBusy(true);
return poolConnection;
}
}
return null;
}
}
- 線程池管理類
使用單例
public class PoolManager {
/**
* 連接池持有類
*/
private static class PoolHolder {
private static String url = "";
private static String driver = "";
private static String username = "";
private static String password = "";
private static int size = 10;
private static IPool poolImpl = new PoolImpl(driver, url, username, password, size);
}
/**
* 內部類單利模式產生使用對象
* @return 單例
*/
public static IPool getInstance() {
return PoolHolder.poolImpl;
}
}