「數據庫密集型」意味著應用與 DB 高頻、短連接交互。無池模式下:
UCP (Universal Connection Pool) 提供**工業級池化能力**:預熱、動態伸縮、驗證、洩漏回收、統計監控、RAC 負載感知 (FCF)。
import java.sql.*;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
public class UcpDemo {
public static void main(String[] args) throws SQLException {
// 1. 創建池數據源
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
// 2. 連接屬性
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@//localhost:1521/XEPDB1");
pds.setUser("hr");
pds.setPassword("hr");
// 3. 池屬性 (核心調優點)
pds.setInitialPoolSize(5); // 啟動預熱
pds.setMinPoolSize(5); // 保持最小
pds.setMaxPoolSize(20); // 上限
pds.setValidateConnectionOnBorrow(true); // 借用前驗證,防死連
System.out.println("--- 池初始化完成 ---");
printStats(pds, "初始狀態");
// 4. 借用連接、執行查詢、歸還 (try-with-resources 自動 close 歸還)
try (Connection conn = pds.getConnection()) {
System.out.println("\n>>> 連接借用成功");
printStats(pds, "借用後");
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT user FROM dual")) {
while (rs.next()) {
System.out.println("當前用戶: " + rs.getString(1));
}
}
} // conn.close() 這裡執行,實際歸還池而非關閉物理連接
System.out.println("\n>>> 連接已歸還");
printStats(pds, "歸還後");
}
static void printStats(PoolDataSource pds, String label) throws SQLException {
System.out.printf("[%s] 可用:%d | 借用:%d | 總連接:%d%n",
label,
pds.getAvailableConnectionsCount(),
pds.getBorrowedConnectionsCount(),
pds.getAvailableConnectionsCount() + pds.getBorrowedConnectionsCount()
);
}
}
[初始狀態] 可用:5 | 借用:0 | 總連接:5
>>> 連接借用成功
[借用後] 可用:4 | 借用:1 | 總連接:5
當前用戶: HR
>>> 連接已歸還
[歸還後] 可用:5 | 借用:0 | 總連接:5
關鍵驗證點: 歸還後 `Available` 恢復初始值,`Borrowed` 歸零,**物理連接被復用**,未重建。
| 參數 | 推薦值 | 調優邏輯 |
|---|---|---|
| InitialPoolSize | CPU 核心數 * 2 | 預熱避免冷啟動抖動 |
| MinPoolSize | = Initial | 防止收縮導致後續借用等待創建 |
| MaxPoolSize | DB `processes` * 0.7 / 實例數 | 留 30% 給後台作業、管理連接 |
| ValidateConnectionOnBorrow | true | **必開**,代價極低 (單次 SELECT 1),收益極大 (剔除防火牆斷開/DB 重啟導致的死連) |
| InactiveConnectionTimeout | 300-600 秒 | 釋放長期閒置連接,配合 MinPoolSize 保持基線 |
| AbandonedConnectionTimeout | 60-120 秒 | 自動回收應用忘記 close() 的「棄用連接」,防洩漏耗盡池 |
| TimeToLiveConnectionTimeout | 3600-7200 秒 | 強制輪換長壽命連接,防止客戶端狀態污染、修補 DB 端游標泄漏 |
| ConnectionWaitTimeout | 5-10 秒 | 池滿時等待上限,超時快速失敗避免線程堆積 |
| FastConnectionFailoverEnabled | true (RAC) | 訂閱 FAN 事件,實例故障毫秒級感知、清理失效連接 |
| ONSConfiguration | `nodes=rac1:6200,rac2:6200` | RAC 環境必配,啟用 FCF 前提 |
場景:應用需設置 `NLS_LANGUAGE`、`CLIENT_IDENTIFIER`、PL/SQL 包變量等會話狀態。頻繁重置開銷大。
// 借用時指定標籤,池優先返回匹配標籤的連接
String label = "NLS_LANG=AMERICAN_AMERICA.AL32UTF8;CLIENT_ID=ORDER_SERVICE";
Connection conn = pds.getConnection(label);
// 使用完歸還時攜帶標籤 (可選,池會記錄連接當前狀態)
((oracle.ucp.jdbc.LabelableConnection) conn).registerConnectionLabel("NLS_LANG=AMERICAN_AMERICA.AL32UTF8");
conn.close(); // 歸還池,標籤保留
池內部維護 `Label -> Connection` 映射,顯著減少 `ALTER SESSION` / `DBMS_SESSION.SET_IDENTIFIER` 開銷。
// 1. 編程式獲取詳細統計
UniversalConnectionPoolManager mgr = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager();
UniversalConnectionPool pool = mgr.getConnectionPool("APP_MAIN_POOL"); // 需 setConnectionPoolName
System.out.println("總借用次數: " + pool.getStatistics().getTotalConnectionBorrowedCount());
System.out.println("總等待時間(ms): " + pool.getStatistics().getTotalConnectionWaitTime());
System.out.println("平均借用等待(ms): " + pool.getStatistics().getAverageBorrowedWaitTime());
System.out.println("洩漏回收數: " + pool.getStatistics().getAbandonedConnectionCount());
// 2. JMX 暴露 (啟動 JVM 參數 -Dcom.sun.management.jmxremote)
// ObjectName: oracle.ucp.admin:type=UniversalConnectionPool,name=APP_MAIN_POOL
// 可接入 Prometheus JMX Exporter / Grafana 告警
| 反模式 | 後果 | 修正 |
|---|---|---|
| 每次請求 `new PoolDataSource()` | 創建大量獨立池,連接不可復用,DB 會話爆炸 | PoolDataSource **單例** (Spring `@Bean` / 靜態持有) |
| 借用後不 `close()` / 不 `try-with-resources` | 連接洩漏,池耗盡,後續請求超時/失敗 | 強制 `try (Connection c = pds.getConnection())` 模式 |
| 長事務中持有連接做業務邏輯/遠程調用 | 佔用池連接時間長,併發能力驟降,鎖爭用劇增 | 事務邊界極小化:借連接 → 開始事務 → SQL 批次 → 提交 → 歸還 |
| 在連接上設置 `AutoCommit=false` 後歸還不提交/回滾 | 污染池連接,下一個借用者繼承脏事務狀態 | 歸還前顯式 `commit()` 或 `rollback()`;或依賴 UCP `setValidateConnectionOnBorrow` 隱式驗證 |