JDBC Connection Management#
JDBC (Java Database Connectivity) API 提供了一個與資料庫伺服器通訊的通用介面。所有網路邏輯與資料庫特定的通訊協定都隱藏在廠商無關的 JDBC API 背後。java.sql.Driver 是與 JDBC API 互動的主要入口點,負責定義實作版本細節並提供資料庫連線的存取。
JDBC 定義了四種驅動類型,其中 Type 4(純 Java 實作資料庫通訊協定)因為易於設定與除錯,是最常被採用的方案。

Figure 2.1: JDBC plugin architecture
DriverManager#
DriverManager 提供以下方法來取得資料庫連線:
getConnection(String url, Properties info)getConnection(String url, String user, String password)getConnection(String url)
每次呼叫 getConnection() 時,DriverManager 都會向底層 Driver 請求一個新的實體連線 (physical connection)。這個過程涉及建立 TCP socket 連線到資料庫伺服器。

Figure 2.2: DriverManager connection
JDBC 最初於 1997 年推出時,僅支援 DriverManager 來取得連線,適用於當時常見的 two-tier 架構:每個桌面應用程式實例各自擁有一條專屬的資料庫連線。

Figure 2.3: Two-tier architecture
自 JDBC 4.0 起,Service Provider Interfaces 機制可以自動發現 classpath 中所有可用的驅動程式,不再需要手動載入驅動。
DataSource#
1999 年 J2EE 與 JDBC 2.0 一同推出,標誌著 Enterprise Java 的開始。企業應用程式採用 three-tier 架構,中間層作為使用者請求與各種資料來源(如關聯式資料庫、messaging queues)之間的橋樑。

Figure 2.4: Three-tier architecture
如果說 DriverManager 是實體連線的工廠,那麼 javax.sql.DataSource 介面就是邏輯連線 (logical connection) 的提供者:
- Application server 提供的是邏輯連線(proxy 或 handle),而非實體連線
- 這使得中間層可以攔截並記錄客戶端對連線物件的互動
- 在 JTA 環境中,application server 可以裝飾連線 handle 以支援分散式交易語意
沒有 connection pooling 的 DataSource 工作流程#
- 應用程式向
DataSource請求連線 DataSource使用底層驅動開啟實體連線並建立 TCP socket- 應用程式使用連線執行 SQL 陳述式
- 關閉連線時,同時關閉底層的 TCP socket

Figure 2.5: DataSource without connection pooling
頻繁開啟與關閉資料庫連線是非常昂貴的操作。重用連線可以避免建立 TCP 連線的開銷、保留記憶體緩衝區,並減少客戶端 JVM 物件的垃圾回收。
為什麼 Connection Pooling 快這麼多?#
Connection pooling 的核心機制如下:
- 當請求連線時,pool 先尋找未配置的連線 (unallocated connection)
- 若有空閒連線,直接交給客戶端使用
- 若沒有空閒連線,pool 嘗試增長到最大允許大小
- 若已達最大值,請求會被排隊等待,超時後重試或拋出例外
- 客戶端關閉邏輯連線時,連線歸還 pool 而非真正關閉實體連線

Figure 2.6: Connection acquisition request flow
效能比較#
書中測試比較了開關 1000 次資料庫連線的總耗時:
| DB_A | DB_B | DB_C | DB_D | HikariCP | |
|---|---|---|---|---|---|
| mean (ms) | 13.829 | 6.477 | 28.910 | 1.590 | 0.003458 |
| p99 (ms) | 20.432 | 9.944 | 54.952 | 3.022 | 0.010263 |
使用 connection pooling 後,連線取得時間快了 二到四個數量級。

Figure 2.7: DataSource connection
Connection pool 不僅減少連線取得時間,還能限制應用程式同時使用的連線數量,作為有界緩衝區來緩衝流量尖峰,避免耗盡所有資料庫資源。
Queuing Theory 容量規劃#
Little’s Law 是一個適用於穩態排隊系統的通用公式:
- L = lambda x W
L:系統中的平均請求數(包含正在服務與排隊中的)lambda:長期平均到達率W:請求在系統中的平均停留時間
實際範例#
假設交易平均回應時間為 100ms,平均連線請求率為每秒 50 個:
L = 50 x 0.1 = 5個連線請求- 因此 pool size 設為 5 即可在不排隊的情況下應付平均流量
在 queuing theory 中,throughput(離開率) 表示為:
mu = Ls / Ws = pool size / connection lease time
當到達率超過離開率時,溢出的請求必須排隊等待。書中示範了一個每秒 250 個請求、持續 3 秒的流量尖峰情境:750 個請求需要 15 秒才能完全處理,queue 大小成長到 700 筆。

Figure 2.8: Little's Law queue
Little’s Law 基於長期平均值,可能無法適當反映間歇性的流量爆發。在實際場景中,connection pool 必須能適應短期的流量尖峰,因此考量實際的 pool throughput 至關重要。
實務資料庫連線配置#
即使 queuing theory 提供了理論基礎,企業系統的動態行為遠比通用公式所能表達的複雜。持續監控連線使用模式並據此調整 pool 大小,才是務實的做法。
FlexyPool 監控指標#
作者開發的 FlexyPool 開源專案支援主流 connection pooling 框架,提供以下關鍵指標:
- concurrent connection requests:同時被請求的連線數

Figure 2.9: Concurrent connection requests
- concurrent connections:同時被使用的連線數

Figure 2.10: Concurrent connections
- maximum pool size:pool 大小隨時間的變化

Figure 2.11: Maximum pool size
- connection acquisition time:從 DataSource 取得連線的耗時

Figure 2.12: Connection acquisition time
- retry attempts:連線取得的重試次數

Figure 2.13: Retry attempts
- overall connection acquisition time:包含重試在內的總取得時間

Figure 2.14: Overall connection acquisition time
- overflow pool size:pool 超過最大值的溢出量
- connection lease time:連線從取得到釋放的持續時間

Figure 2.15: Connection lease time
FlexyPool Failover 策略#
- Increment pool size on timeout:連線取得超時時自動增長 pool,直到 overflow 上限
- Retrying attempts:對缺乏重試機制的 pool 提供自動重試功能
連線 Pool 監控的實務要點#
- 平均值會抹平所有異常值,無法反映流量尖峰的影響
- 百分位數 (percentiles) 在應用程式效能監控中更有價值,能揭示離群值並捕捉流量變化的即時效果
- p99 concurrent connections 比平均值更適合用來決定正確的 pool 大小
- 設定 idle connection timeout 很重要,讓 pool 能釋放未使用的連線給其他客戶端
長時間持有連線會增加其他客戶端的 connection acquisition time。長時間執行的交易可能持有資料庫鎖,增加序列化比例、阻礙平行處理。解決方案包括正確建立索引以加速慢查詢,或將 application-level transaction 拆分為多個較短的資料庫交易。