MySQL 自己經過 show slave status 提供了 Seconds_Behind_Master ,用於衡量主備之間的複製延遲,可是今天碰到了一個場景,發現 Seconds_Behind_Master 爲 0 , 備庫的 show slave status 顯示 IO/SQL 線程都是正常的 , MySQL 的主庫上的變動卻長時間沒法同步到備庫上。若是沒有人爲干預,直到一個小時之後, MySQL 纔會自動重連主庫,繼續複製主庫的變動。程序員
影響範圍: MySQL , Percona , MariaDB 的全部版本。數據庫
雖然這種場景很是特殊,遇到的機率並不高,可是我的以爲有必要提醒一下使用 MySQL 的 DBA 們。經過對這個場景的分析,也有助於咱們更加深刻的理解 MySQL replication 重試機制。網絡
搭建主備的複製,臨時斷開主庫的網絡,並 kill 掉主庫 MySQL 的 binlog dump 線程。ide
此時觀察備庫的複製狀況, show slave status 中:spa
Slave_IO_Running: Yes線程
Slave_SQL_Running: Yesorm
Seconds_Behind_Master: 0blog
可是此時你把網絡恢復之後,在主庫作任何變動,備庫都沒法得到數據更新了。並且備庫上的show slave status 顯示: IO 線程 SQL 線程一切正常,複製延遲一直是 0 。ci
一切正常,普通的監控軟件都不會發現備庫有數據延遲。資源
MySQL 的 Replication 是區別於其餘數據庫很關鍵的地方。也是可擴展性和高可用的基礎。它自己已經很是智能化,只須要咱們調用 Change Master 指定 Binlog 文件名和偏移位置就能夠搭建從主庫到備庫的複製關係。
MySQL 複製 線程 會自動將目前複製位置記錄下來,在主備複製中斷的時候自動連上主庫,並從上次中斷的位置從新開始複製。這些操做都是全自動化的,不須要人爲的干預。這給了 MySQL DBA 帶來了不少便利,同時卻也隱藏了不少細節。
要真正的理解前面問題的真相以及怎麼解決這個問題,咱們仍是須要真正的理解 MySQL 複製的原理。
首先, MySQL 的複製是「推」的,而不是「拉」的。「拉」是指 MySQL 的備庫不斷的循環詢問主庫是否有數據更新,這種方式資源消耗多,而且效率低。「推」是指 MySQL 的主庫在本身有數據更新的時候推送這個變動給備庫,這種方式只有在數據有變動的時候纔會發生交互,資源消耗少。若是你是程序員出身,你必定會選擇「推」的方式。
那麼 MySQL 具體是怎麼「推」的列,實際上備庫在向主庫申請數據變動記錄的時候,須要指定從主庫Binlog 的哪一個文件 ( MASTER_LOG_FILE ) 的具體多少個字節偏移位置 ( MASTER_LOG_POS ) 。對應的,主庫會啓動一個 Binlog dump 的線程,將變動的記錄從這個位置開始一條一條的發給備庫。備庫一直監聽主庫過來的變動,接收到一條,纔會在本地應用這個數據變動。
從上面的分析,咱們能夠大體猜到爲何 show slave status 顯示一切正常,可是實際上主庫的變動都沒法同步到備庫上來:
出現問題的時候, Binlog dump 程序被咱們 kill 掉了。做爲監聽的一方,備庫一直沒有收到任何變動,它會認爲主庫上長時間沒有任何變動,致使沒有變動數據推送過來。備庫是沒法判斷主庫上對應的Binlog dump 線程 究竟是意外終止了,仍是長時間沒有任何數據變動的。因此,對這兩種狀況來講,備庫都顯示爲正常。
固然, MySQL 會盡可能避免這種狀況。好比:
l 在 Binlog dump 被 kill 掉時通知備庫 線程 被 kill 掉了。因此咱們重現時須要保證這個通知發送不到備庫,也就是說該問題重現的關鍵在於 Binlog dump 被 kill 的消息因爲網絡堵塞或者其餘緣由沒法發送到備庫。
l 備庫若是長時間沒有收到從主庫過來的變動,它會每隔一段時間重連主庫。
基於上面的分析,咱們知道 MySQL 在這種狀況下確實沒法避免,那麼咱們能夠有哪些辦法能夠避開列:
1. 被動處理:修改延遲的監控方法,發現問題及時處理。
2. 主動預防:正確設置 --master-retry-count , --master-connect-retry , --slave-net-timeout 複製重試參數。
l 被動處理
MySQL 的延遲監控大部分直接採集 show slave status 中的 Seconds_Behind_Master 。這種狀況下,Seconds_Behind_Master 就沒法用來真實的衡量主備之間的複製延遲了。咱們建議經過在主庫輪詢插入時間信息,並經過複製到備庫的時間差來得到主備延遲的方案。 Percona 提供了一種相似的方案 pt-heartbeat 。
發現這個問題之後,咱們只須要 stop slave; start slave; 重啓複製就能解決這個問題。
l 主動預防
MySQL 能夠指定三個參數,用於複製線程重連主庫: --master-retry-count , --master-connect-retry , --slave-net-timeout 。
其中 master-connect-retry 和 master-retry-count 須要在 Change Master 搭建主備複製時指定,而 slave-net-timeout 是一個全局變量,能夠在 MySQL 運行時在線設置。
具體的重試策略爲:備庫過了 slave-net-timeout 秒尚未收到主庫來的數據,它就會開始第一次重試。而後每過 master-connect-retry 秒,備庫會再次嘗試重連主庫。直到重試了 master-retry-count 次,它纔會放棄重試。若是重試的過程當中,連上了主庫,那麼它認爲當前主庫是好的,又會開始 slave-net-timeout 秒的等待。
slave-net-timeout 的默認值是 3600 秒, master-connect-retry 默認爲 60 秒, master-retry-count 默認爲 86400 次。也就是說,若是主庫一個小時都沒有任何數據變動發送過來,備庫纔會嘗試重連主庫。這就是爲何在咱們模擬的場景下,一個小時後,備庫纔會重連主庫,繼續同步數據變動的緣由。
這樣的話,若是你的主庫上變動比較頻繁,能夠考慮將 slave-net-timeout 設置的小一點,避免主庫Binlog dump 線程 終止了,沒法將最新的更新推送過來。
固然 slave-net-timeout 設置的太小也有問題,這樣會致使若是主庫的變動確實比較少的時候,備庫頻繁的從新鏈接主庫,形成資源浪費。
沃趣科技的 Q Monitor 監控中對主備複製的延遲監控,並非經過 Seconds_Behind_Master 來監控主備的。它採用了相似於 pt-heartbeat 的方式對主備進行復制延遲監控。