資料庫連線建立/關閉開銷巨大,頻繁操作會拖垮系統效能。本文提出基於「資源池」與「引用計數」設計模式的連線池架構,深入解決事務獨佔連線、多線程併發安全、連線封裝統一介面等核心難題,提供可落地的高效管理方案。
簡單應用可「用完即關」,但高併發系統中,連線頻繁創建銷毀導致:DB 進程抖動、網路握手延遲、JVM GC 壓力。核心解法:**連線復用** — 建立靜態連線池,初始化預建連線,運行期僅借/還,不銷毀。
Vector/Stack 等容器
// 借連線
public Connection borrowConnection() {
synchronized (idlePool) {
if (!idlePool.isEmpty()) {
Connection conn = idlePool.remove(0);
activePool.add(conn);
conn.incrementRefCount(); // 引用計數+1
return wrap(conn); // 封裝後返回
}
}
// 閒置池耗盡:策略性從使用池借用 (如引用計數最小者)
return borrowFromActivePool();
}
// 還連線
public void returnConnection(Connection conn) {
Connection raw = unwrap(conn);
if (raw.getRefCount() <= 1) { // 僅當前使用者
synchronized (idlePool) {
activePool.remove(raw);
idlePool.add(raw);
}
} else {
raw.decrementRefCount(); // 共用連線,僅減計數
}
}
refCount,記錄共用者數量refCount 最小的連線 (減少衝突)ConnectionSelectionStrategy) 支援插拔式演算法 (LRU、隨機、負載均衡等)難點:事務要求 ACID,多操作必須同一連線,且不能被其他非事務操作干擾。
方案:顯式事務邊界 + 事務登記表
beginTransaction():從閒置池取連線,setAutoCommit(false),登記 Thread -> Connection 映射commit()/rollback():結束事務,移除登記,連線歸還閒置池begin 時增加「巢狀層級」計數,最外層 commit 才真正提交
public interface DbConnection {
Statement createStatement();
PreparedStatement prepareStatement(String sql);
void commit(); void rollback(); void close(); // 等標準方法
}
// 普通連線封裝:借用後自動歸還池
public class PooledConnection implements DbConnection { ... }
// 事務連線封裝:禁用自動提交、關閉時不歸還池
public class TransactionConnection implements DbConnection { ... }
上層代碼僅依賴 DbConnection 介面,透過工廠方法取得實例,完全屏蔽池化邏輯。
synchronized 或使用 ReentrantLock/ConcurrentHashMap| 參數 | 建議值 | 說明 |
|---|---|---|
| initialPoolSize | CPU核心數 * 2 | 啟動預熱連線數 |
| maxPoolSize | 根據DB最大連線數/應用峰值QPS估算 | 上限防雪崩 |
| maxWaitTime | 3-5秒 | 借連線超時,避免無限阻塞 |
| validationQuery | SELECT 1 | 定期/借用前檢測連線有效性 |
| idleTimeout | 30分鐘 | 閒置連線回收,防DB端斷開 |
但底層原理 (資源池、引用計數、事務隔離、封裝多態) 與成熟產品 (HikariCP, Druid, DBCP, Tomcat JDBC Pool) 一致,理解本文架構有助於深度使用與故障診斷商用連線池。