使用 Percona Toolkit 的 pt-heartbeat 監控 MySQL 主從延遲時,若主庫(Master)宕機,工具會持續重試連線。然而,這導致進程記憶體緩慢但持續增長,長時間運行將耗盡系統資源。
pt-heartbeat --update -h 192.168.244.10 -u monitor -p monitor123 -D test --create-table
透過 top -p <PID> 與 pmap 監控,發現:




問題出自 get_dbh 函數的重試邏輯:
my $tries = 2;
while ( !$dbh && $tries-- ) {
$dbh = eval { DBI->connect($cxn_string, $user, $pass, $defaults) };
if ( !$dbh && $EVAL_ERROR ) {
# ... 錯誤處理 ...
if ( !$tries ) {
die $EVAL_ERROR; # <== 關鍵問題在此
}
}
}
當 $tries 歸零時,die $EVAL_ERROR 未能拋出異常並終止腳本,導致進程進入無限重試迴圈,且每次失敗的 DBI 連線嘗試都會殘留記憶體分配,造成洩漏。
驗證方式:在 die 前加入除錯輸出,發現 PTDEBUG && _d("$EVAL_ERROR") 執行了,但 die 無效果。
將 die $EVAL_ERROR 修改為字串拼接形式:
if ( !$tries ) {
die "test:$EVAL_ERROR"; # 強制字串化上下文
}
修改後測試:主庫關閉時,pt-heartbeat 正常異常退出,記憶體不再增長。

極可能與 Perl 版本(v5.10.1)對 die 參數處理的特定行為有關。當 $EVAL_ERROR 為特定物件或帶有過載操作時,純變量傳遞可能不觸發異常拋出機制。
已提交至 Percona 官方 Bug 追蹤系統:
https://bugs.launchpad.net/percona-toolkit/+bug/1629164
ulimit -v 或 cgroup)防止單進程耗盡資源此問題揭示了 Perl 異常處理的微妙行為差異。在編寫高可用監控工具時,重試邏輯必須確保「失敗即退出」的語義落地,避免靜默失敗導致資源洩漏。