錯誤特徵:應用啟動正常,隔天早上第一波請求噴 `Communications link failure / No operations allowed after connection closed`,重啟 Tomcat 即恢復——經典的 MySQL `wait_timeout` 預設 8 小時 (28800 秒) 主動斷開閒置連接。
連接池 (DBCP/HikariCP/Tomcat JDBC Pool) 借出的連接長時間未使用,MySQL Server 端已關閉 TCP 連線,連接池不知情,下次借用時直接拿失效連接執行 SQL,拋出 `SocketException`。
| 層級 | 配置關鍵 | 推薦值 |
|---|---|---|
| 連接池心跳 | `testWhileIdle=true` `timeBetweenEvictionRunsMillis=30000` `minEvictableIdleTimeMillis=1800000` (30分) | 每 30 秒掃描閒置連接,閒置超過 30 分驅逐 |
| 借用前驗證 | `testOnBorrow=true` `validationQuery=SELECT 1` `validationQueryTimeout=3` | 每次 `getConnection()` 執行輕量查詢,失敗則丟棄重建 |
| 驅動層自動重連 | JDBC URL 附加: `?autoReconnect=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Taipei` | 驅動偵測斷線自動重連 (僅適用舊版驅動 5.1.x,8.0+ 已棄用,靠連接池即可) |
spring.datasource.hikari:
minimum-idle: 5
maximum-pool-size: 20
idle-timeout: 300000 # 5 分鐘
max-lifetime: 1800000 # 30 分鐘,必須 < MySQL wait_timeout
connection-timeout: 30000
connection-test-query: SELECT 1
validation-timeout: 3000
<Resource name="jdbc/mydb"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Taipei"
username="root" password="pwd"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"
minEvictableIdleTimeMillis="1800000"
testOnBorrow="true"
validationQuery="SELECT 1"
validationQueryTimeout="3"
maxActive="20" maxIdle="10" minIdle="5" />
配好上述三層,徹底告別「每天早上重啟一次」的惡夢。