MySQL 連線池耗盡常見原因:**大量 Sleep 狀態連線佔用**,且 `Time` 值極大(超過 `wait_timeout`/`interactive_timeout` 仍未釋放)。根治需修正應用程式連線洩漏(未正確 `close`、持久連線濫用),但作為緊急緩解與防禦性備援,定時 Kill 長時間 Sleep 連線是實用手段。本文提供 PHP 腳本 + Crontab 部署方案。
<?php
/**
* Kill MySQL 長時間 Sleep 連線
* 依賴:mysqli 擴充套件
* 權限:執行帳號需有 PROCESS、SUPER 權限(或至少 KILL 權限)
*/
$dbHost = 'localhost';
$dbUser = 'root';
$dbPass = 'your_password'; // 建議從環境變數/配置檔讀取
$maxSleepTime = 120; // 秒,超過此值視為死連線
$mysqli = new mysqli($dbHost, $dbUser, $dbPass);
if ($mysqli->connect_errno) {
error_log("[KillSleep] 連線失敗: {$mysqli->connect_error}");
exit(1);
}
// 查詢進程列表
$sql = "SHOW FULL PROCESSLIST";
$res = $mysqli->query($sql);
if (!$res) {
error_log("[KillSleep] 查詢失敗: {$mysqli->error}");
exit(1);
}
$killed = 0;
while ($row = $res->fetch_assoc()) {
// 僅處理 Sleep 狀態
if ($row['Command'] === 'Sleep' && $row['Time'] > $maxSleepTime) {
$id = (int)$row['Id'];
// 排除自己的連線、複製線程、系統線程
if ($id === $mysqli->thread_id) continue;
$killSql = "KILL $id";
if ($mysqli->query($killSql)) {
$killed++;
error_log("[KillSleep] Killed process $id (User: {$row['User']}, Host: {$row['Host']}, DB: {$row['db']}, Time: {$row['Time']}s, Info: {$row['Info']})");
} else {
error_log("[KillSleep] Kill $id 失敗: {$mysqli->error}");
}
}
}
$res->free();
$mysqli->close();
if ($killed > 0) {
error_log("[KillSleep] 完成,共清理 $killed 條睡眠連線");
}
?>
/usr/local/sbin/kill-sleep.php。# 每 2 分鐘檢查一次 */2 * * * * /usr/bin/php /usr/local/sbin/kill-sleep.php >> /var/log/kill-sleep.log 2>&1
| 參數 | 建議值 | 說明 |
|---|---|---|
| maxSleepTime | 120~300 秒 | 視業務長連線特性調整,太短會誤殺正常長輪詢 |
| Crontab 頻率 | 1~5 分鐘 | 頻率過高增加負載,過低清理不及時 |
| MySQL wait_timeout | 28800 (8小時) | 預設值,應用層應在遠小於此值時主動釋放 |
| MySQL max_connections | 1000+ | 配合應用連線池上限設定,避免單點耗盡 |
更多 MySQL 連線池優化、PHP 持久連線陷阱、Swoole 連線管理、Prometheus 監控 MySQL、PT-Kill 工具替代方案、資料庫代理 中間件、連線洩漏排查實戰,請參考本站專題系列。