
十五年數(shù)據(jù)庫相關(guān)經(jīng)驗做過 DBA、架構(gòu)師、技術(shù)顧問。不求顛覆只求靠譜。主從同步這個事看起來簡單。主庫寫、備庫讀binlog 傳過去、relay log 回放完齊活。但真出問題時能要人命。上個月接到一個告警業(yè)務(wù)方反饋剛下的訂單查不到。排查下來是主從延遲 30 秒——用戶剛在主庫寫完備庫還沒同步到。30 秒在互聯(lián)網(wǎng)業(yè)務(wù)里就是數(shù)據(jù)丟了。干了這么多年 DBA主從延遲是最容易被低估、也最容易背鍋的問題。今天把排查方法論整理出來從發(fā)現(xiàn)延遲到定位根因到解決每一步都說清楚。有好消息也有踩坑的地方。各位耐心看完。01 延遲從哪來先搞清主從同步的完整鏈路排查延遲之前先搞清楚延遲可能出現(xiàn)在哪個環(huán)節(jié)。主從同步不是主庫寫完備庫立刻就有它是一整條鏈路第一步主庫寫 binlog。主庫執(zhí)行寫操作后把變更記錄寫入 binlog 文件。這一步通常很快毫秒級。第二步binlog 傳輸?shù)絺鋷?。備庫?I/O 線程連接主庫拉取 binlog寫入備庫的 relay log。這一步的延遲取決于網(wǎng)絡(luò)帶寬和 binlog 產(chǎn)生速度。第三步備庫回放 relay log。備庫的 SQL 線程或者多線程復(fù)制的工作線程讀取 relay log把變更重放到備庫數(shù)據(jù)上。這一步是延遲的重災(zāi)區(qū)。第四步備庫數(shù)據(jù)可見。回放完成后新數(shù)據(jù)才能在備庫上被查詢到。關(guān)鍵認(rèn)知主從延遲不是一個數(shù)字是整條鏈路累積的結(jié)果。排查時要逐段確認(rèn)延遲出在哪個環(huán)節(jié)。02 怎么發(fā)現(xiàn)延遲別等業(yè)務(wù)方來報主從延遲最可怕的不是有延遲是有延遲但沒人知道。等業(yè)務(wù)方反饋剛寫的數(shù)據(jù)查不到問題已經(jīng)發(fā)生很久了。DBA 應(yīng)該在業(yè)務(wù)感知之前就發(fā)現(xiàn)并處理。監(jiān)控指標(biāo)看這三個數(shù)就夠了MySQL 8.0查詢performance_schema.replication_connection_status和replication_applier_status關(guān)注LAST_HEARTBEAT_TIMESTAMP最后一次心跳時間和SERVICE_STATE復(fù)制狀態(tài)。MySQL 5.7執(zhí)行SHOW SLAVE STATUS關(guān)注Seconds_Behind_Master延遲秒數(shù)和Slave_IO_Running/Slave_SQL_Running復(fù)制線程狀態(tài)。PostgreSQL查詢pg_stat_replication關(guān)注write_lag、flush_lag、replay_lag分別對應(yīng)寫入、刷盤、回放延遲。三個閾值分三級告警延遲級別閾值響應(yīng)動作正常 1 秒無需處理常規(guī)監(jiān)控警告1-10 秒關(guān)注趨勢準(zhǔn)備介入危險 10 秒立即排查必要時切主我的習(xí)慣告警不要只設(shè)一個閾值。設(shè)三級讓團(tuán)隊知道現(xiàn)在是什么狀態(tài)。只設(shè)一個延遲 10 秒告警等告警響了再查黃花菜都涼了。03 排查流程——四步定位法發(fā)現(xiàn)延遲后按這個流程走90% 的主從延遲都能定位到根因。第一步確認(rèn)延遲在主庫還是備庫怎么看在主庫執(zhí)行一個寫入操作記錄時間戳。然后在備庫查詢這條數(shù)據(jù)記錄看到的時間戳。兩者之差就是端到端延遲。如果延遲很大但主庫的 binlog 寫入很快查 binlog 文件大小增長速度說明問題不在主庫在傳輸或回放環(huán)節(jié)。第二步確認(rèn)是傳輸慢還是回放慢怎么看MySQL 執(zhí)行SHOW SLAVE STATUS對比兩個值Read_Master_Log_PosI/O 線程讀到的主庫 binlog 位置Exec_Master_Log_PosSQL 線程回放到的位置如果Read_Master_Log_Pos落后Master_Log_Pos主庫當(dāng)前位置很多說明傳輸慢——網(wǎng)絡(luò)帶寬不夠或者主庫 binlog 產(chǎn)生太快。如果Read_Master_Log_Pos跟得上但Exec_Master_Log_Pos落后很多說明回放慢——備庫 SQL 線程處理不過來。第三步定位回放慢的具體原因回放慢是最常見的延遲原因。具體又分幾種情況情況 A大事務(wù)回放。主庫執(zhí)行了一個大事務(wù)比如批量更新了 100 萬行備庫回放這個事務(wù)時SQL 線程被占住其他小事務(wù)都得排隊等。怎么看查SHOW PROCESSLIST看備庫 SQL 線程正在執(zhí)行什么 SQL。如果是一條執(zhí)行了幾十秒還沒完的 UPDATE 或 DELETE基本就是大事務(wù)了。情況 B鎖沖突。備庫回放時遇到鎖沖突SQL 線程被阻塞。怎么看查備庫的information_schema.innodb_trx和data_lock_waits看有沒有鎖等待。情況 C備庫硬件跟不上。主庫用 SSD備庫用的是機(jī)械盤。同樣的寫入量備庫回放速度天然就慢。怎么看對比主備兩端的磁盤 I/O 指標(biāo)iostat 看 iowait 和吞吐量。如果備庫磁盤利用率持續(xù) 100%就是硬件瓶頸。第四步驗證根因定位到可能的根因后驗證一下如果是大事務(wù)在測試環(huán)境模擬同樣規(guī)模的事務(wù)看備庫回放耗時。如果是鎖沖突分析鎖等待鏈確認(rèn)阻塞源。如果是硬件瓶頸做磁盤 I/O 壓測確認(rèn)備庫磁盤確實扛不住。經(jīng)驗不要猜根因要驗證。我見過太多 DBA 憑經(jīng)驗覺得是大事務(wù)導(dǎo)致的結(jié)果查半天發(fā)現(xiàn)是備庫磁盤滿了。04 解決思路——對癥下藥定位到根因后解決思路就清晰了。方案 A大事務(wù) → 拆分事務(wù) 并行復(fù)制大事務(wù)是主從延遲的頭號殺手。一個事務(wù)更新了 100 萬行備庫 SQL 線程要一條一條回放可能花幾十分鐘。治標(biāo)把大事務(wù)拆成小事務(wù)。原來一個 UPDATE 更新 100 萬行改成每次更新 1 萬行分 100 次執(zhí)行。每次持鎖時間短備庫回放也快。治本開啟并行復(fù)制。MySQL 5.7 支持多線程復(fù)制MTS備庫可以用多個線程并行回放不同庫或不同事務(wù)的變更。-- MySQL 開啟并行復(fù)制基于庫的并行SETGLOBALslave_parallel_workers4;-- MySQL 5.7 基于邏輯時鐘的并行復(fù)制推薦SETGLOBALslave_parallel_typeLOGICAL_CLOCK;SETGLOBALslave_parallel_workers8;注意并行復(fù)制不是線程越多越好。線程數(shù)超過 CPU 核心數(shù)后收益遞減。一般設(shè)成 CPU 核心數(shù)的 1-2 倍。方案 B網(wǎng)絡(luò)傳輸慢 → 壓縮 帶寬擴(kuò)容如果確認(rèn)是 binlog 傳輸慢兩個方向解決開啟 binlog 壓縮MySQL 5.7 支持 binlog 傳輸壓縮在網(wǎng)絡(luò)帶寬緊張時能顯著降低傳輸量。-- 主庫開啟 binlog 壓縮SETGLOBALbinlog_transaction_dependency_trackingWRITESET;擴(kuò)容網(wǎng)絡(luò)帶寬如果主備跨機(jī)房部署網(wǎng)絡(luò)帶寬是硬瓶頸。該升級就升級別省這個錢。方案 C備庫硬件瓶頸 → 升級存儲 讀寫分離如果備庫硬件確實扛不住兩條路短期把備庫的讀流量切一部分走主庫降低備庫負(fù)載。長期升級備庫存儲。把機(jī)械盤換成 SSD或者上 NVMe。硬件升級的錢比延遲導(dǎo)致業(yè)務(wù)損失的錢少多了。對比三種復(fù)制方案的延遲表現(xiàn)把上面的內(nèi)容做個對比方便選型方案延遲范圍適用場景局限性單線程復(fù)制秒級到分鐘級小數(shù)據(jù)量、低寫入頻率大事務(wù)必延遲基于庫的并行復(fù)制亞秒級到秒級多庫部署、寫入分散單庫大事務(wù)仍然慢基于邏輯時鐘的并行復(fù)制亞秒級生產(chǎn)環(huán)境推薦需要 MySQL 5.7半同步復(fù)制毫秒級但有寫入延遲數(shù)據(jù)零丟失場景主庫寫入變慢選型建議生產(chǎn)環(huán)境優(yōu)先用基于邏輯時鐘的并行復(fù)制MTS。數(shù)據(jù)安全性要求極高的場景用半同步復(fù)制但接受主庫寫入性能的輕微下降。延遲容忍度的判斷框架不是所有場景都需要零延遲。按業(yè)務(wù)容忍度分三檔容忍度高延遲 10 秒可接受后臺報表查詢數(shù)據(jù)分析任務(wù)非實時用戶查詢方案標(biāo)準(zhǔn)異步復(fù)制 常規(guī)監(jiān)控容忍度中延遲 3 秒可接受用戶個人中心查詢商品詳情查詢訂單狀態(tài)查詢方案并行復(fù)制 三級告警 自動切換容忍度低延遲 1 秒可接受賬戶余額查詢實時庫存查詢支付狀態(tài)確認(rèn)方案半同步復(fù)制 強(qiáng)制走主庫查詢 秒級監(jiān)控我的經(jīng)驗不要一刀切要求主從零延遲。不同業(yè)務(wù)場景對延遲的容忍度不同。按場景分級治理比統(tǒng)一高標(biāo)準(zhǔn)更務(wù)實。從主從延遲是玄學(xué)到延遲是可管理的說實話干 DBA 前五年我對主從延遲的態(tài)度是能忍就忍。延遲幾秒嘛業(yè)務(wù)又不會掛。后來接了幾個金融項目才明白延遲不是能不能忍的問題是業(yè)務(wù)允不允許的問題。證券交易系統(tǒng)延遲 1 秒就是數(shù)據(jù)不一致合規(guī)上過不了關(guān)。這個認(rèn)知轉(zhuǎn)變讓我重新審視主從延遲的排查方法。以前是延遲大了再看現(xiàn)在是持續(xù)監(jiān)控、分級治理、提前預(yù)警。主從延遲不是玄學(xué)是可測量、可定位、可優(yōu)化的工程問題。深度分析為什么并行復(fù)制不能解決所有延遲問題很多人以為開了并行復(fù)制就萬事大吉。實際不是這樣。并行復(fù)制解決的是多個小事務(wù)排隊等的問題。但如果來了一個大事務(wù)比如批量刪除 1000 萬條過期日志再多的并行線程也得等這個事務(wù)回放完。根因在于大事務(wù)本身是一個不可分割的原子操作。備庫不能把一個事務(wù)拆成兩半來回放——那會破壞事務(wù)的原子性。所以并行復(fù)制有天花板。它能解決并發(fā)小事務(wù)的延遲解決不了單一大事務(wù)的延遲。真正的解法是從源頭控制事務(wù)大小。應(yīng)用層做批量操作時分批提交。每批 1000-5000 行不要一個事務(wù)搞定。這個道理和 DBA 的日常建議一致事務(wù)盡量短。不僅是為了減少鎖等待也是為了減少主從延遲。主從延遲巡檢清單日常巡檢建議每周執(zhí)行一次1. 延遲趨勢檢查查看過去一周的延遲曲線確認(rèn)沒有持續(xù)增長趨勢如果延遲峰值在上升說明復(fù)制能力在下降需要排查2. 復(fù)制線程狀態(tài)確認(rèn) I/O 線程和 SQL 線程都在運(yùn)行如果有線程停過查錯誤日志看原因3. 大事務(wù)監(jiān)控查看主庫 binlog 中事務(wù)大小的分布如果出現(xiàn)異常大的事務(wù)通知應(yīng)用團(tuán)隊優(yōu)化4. 硬件資源檢查備庫磁盤 I/O 利用率是否接近上限備庫 CPU 和內(nèi)存使用是否正常5. 告警規(guī)則驗證確認(rèn)三級告警閾值配置正確測試告警通道是否正常發(fā)一條測試告警確認(rèn)能收到總結(jié)主從延遲排查就一套流程看監(jiān)控 → 分段定位 → 驗證根因 → 對癥下藥。核心認(rèn)知延遲不是一個數(shù)字是整條鏈路累積的結(jié)果。傳輸慢和回放慢是兩回事不能混為一談。預(yù)防延遲的關(guān)鍵事務(wù)要短、復(fù)制要并行、監(jiān)控要分級。處理過幾十次主從延遲問題每次回到這套流程問題都能解決。不需要什么高級工具耐心和方法論就夠了。后續(xù)我會繼續(xù)分享數(shù)據(jù)庫參數(shù)調(diào)優(yōu)實戰(zhàn)、容量規(guī)劃方法論這些話題跟著我一篇篇學(xué)數(shù)據(jù)庫這塊就沒問題了。有問題評論區(qū)見。十五年數(shù)據(jù)庫領(lǐng)域老炮。關(guān)注我一起把數(shù)據(jù)庫這件事搞明白。