大量 Sleep 狀態連線會佔用 max_connections 額度,導致新請求被拒、資源浪費甚至服務不可用。本文解析三大根因,提供配置調整、程式碼修正與連線池優化三層次解決方案。
mysql_close()/conn.close(),或連線池配置不當導致閒置連線不釋放。wait_timeout (非互動) 或 interactive_timeout (互動) 秒數內無操作,連線仍保持開啟。修改 /etc/my.cnf:
[mysqld]
wait_timeout = 60 # 非互動連線 60 秒無操作自動斷開
interactive_timeout = 60 # 互動式連線 (如 CLI) 同理
或線上熱修改 (重啟後失效):
SET GLOBAL wait_timeout = 60;
SET GLOBAL interactive_timeout = 60;
生產驗證: 將預設 28800 秒 (8小時) 降至 60 秒,Sleep 連線數立即下降 90%+,QPS 穩定。
// PHP 錯誤示範
$link = mysql_pconnect(...); // 持久連線,易殘留 Sleep
// 正確
$link = mysql_connect(...); // 短連線,請求結束自動釋放
// PHP PDO 範例
try {
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_PERSISTENT => false]);
// 業務邏輯
} finally {
$pdo = null; // 顯式釋放
}
// Java HikariCP 關鍵配置
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=30000 // 30秒閒置回收
spring.datasource.hikari.max-lifetime=1800000 // 30分鐘強制換新
spring.datasource.hikari.connection-timeout=5000
所有資料庫操作包裝在 try-finally 或 with / using 區塊,確保異常路徑也能釋放連線。
-- 即時查看
SHOW PROCESSLIST;
-- 或
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND='Sleep';
-- 監控腳本 (每分鐘跑一次)
sleep_count=$(mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'" -sN | awk '{print $2}')
if [ $sleep_count -gt 500 ]; then alert; fi
| 層面 | 動作 | 效果 |
|---|---|---|
| 配置 | 降低 wait_timeout 至 60-300s | 立竿見影清理殘留連線 |
| 程式碼 | 禁用持久連線、確保顯式關閉、正確配置連線池 | 源頭杜絕 Sleep 產生 |
| 架構 | 引入 ProxySQL/MaxScale 連線多工、限流 | 大規模微服務統一治理 |
「配置調優治標,程式碼規範治本」,雙管齊下才能徹底解決 Sleep 連線風暴。