首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

pt-heartbeat 主庫宕機導致記憶體洩漏:根因分析與修復方案

     編輯:關於SqlServer

問題背景

使用 Percona Toolkit 的 pt-heartbeat 監控 MySQL 主從延遲時,若主庫(Master)宕機,工具會持續重試連線。然而,這導致進程記憶體緩慢但持續增長,長時間運行將耗盡系統資源。

環境與複現步驟

  • 工具版本: pt-heartbeat v2.2.19
  • 資料庫: MySQL Community 5.6.31
  • 運行環境: Perl v5.10.1, RHEL 6.7, 記憶體 500MB
  • 架構: MySQL 與 pt-heartbeat 部署於不同主機

啟動監控

pt-heartbeat --update -h 192.168.244.10 -u monitor -p monitor123 -D test --create-table

觀測記憶體增長

透過 top -p <PID> 與 pmap 監控,發現:

  • 正常運行 15 秒:記憶體穩定 3.3%
  • 主庫關閉後:每秒增長 ~4KB(RSS 與 Dirty 頁同步增長)
  • CPU 時間相同時,記憶體從 3.3% 升至 4.4%(約 +5MB)

原始碼根因分析

問題出自 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

臨時緩解建議

  • 升級 pt-heartbeat 至較新版本(若已修復)
  • 使用外部監控腳本(如 systemd、supervisord)管理進程重啟
  • 設定記憶體限制(ulimit -v 或 cgroup)防止單進程耗盡資源

總結

此問題揭示了 Perl 異常處理的微妙行為差異。在編寫高可用監控工具時,重試邏輯必須確保「失敗即退出」的語義落地,避免靜默失敗導致資源洩漏。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved