MySQL 主從同步(5)-同步延遲狀態考量(seconds_behind_master和pt-heartbea)


 

一般情況下,我們是通過"show slave status \G;"提供的Seconds_Behind_Master值來衡量mysql主從同步的延遲情況。具體說明見:mysql主從同步(4)-Slave延遲狀態監控,這種方法在大多數情況下確實是可行的。但是經驗告訴我,僅僅依靠Seconds_Behind_Master的值來監測主從同步數據是否延遲是絕對不可靠的!!!

曾經遇到過的一個坑:
Mysql主從環境部署后,剛開始主從數據同步是沒問題的,也是通過監控Seconds_Behind_Master的值來判斷同步是否延遲。但是運行一段時間后,突然有一天發現,主庫上寫入新數據后,從庫並沒有按時同步過來!!於是,立刻在從庫上執行"show slave status \G;"發現Seconds_Behind_Master為0 ,並且Slave_IO_Running和Slave_SQL_Running線程狀態都是YES也就是說從庫到主庫的連接還在,沒有斷開!但是主庫上的變更數據就是長時間無法同步到從庫上。如果沒有人為干預,直到一個小時以后,從庫才會自動重新連接主庫,進而才繼續同步主庫的變更
發生這種情況時,通過一般的正常監控方式是不會發現從庫有數據延遲。由此可見,僅僅通過Seconds_Behind_Master=0來判斷同步是否延遲顯然是不夠滴.........

發現這個問題以后,我們人工干預的操作只是需要在從庫上執行下面兩步重新復制就能解決此問題:
mysql> stop slave;
mysql> start slave;

重新執行復制后,要盡快修改slave_net_timeout這個參數

之所以要等1小時才能重新同步,是因為slave_net_timeout這個參數默認的就是3600s,它是設置在多少秒沒收到主庫傳來的Binary Logs events之后,從庫認為網絡超時,Slave IO線程會重新連接主庫。

mysql> show variables like 'slave_net_timeout';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| slave_net_timeout | 3600  |
+-------------------+-------+
1 row in set (0.00 sec)

如果在部署mysql主從同步的時候,沒有在從庫這邊設置好slave_net_timeout這個參數,遇到上面的情況,它就會按照默認的3600s(一小時)采取自動重新連接主庫,然后才能繼續同步主庫的變更。這個參數不能設置太大,太大會造成數據庫延遲或者主備庫直接的鏈接異常不能及時發現但是設置太小又會造成主庫沒有數據更新時頻繁重連。
至於slave_net_timeout這個參數究竟設置多少,要根據自己的mysql主庫數據更新的頻繁程度:主庫數據更新頻繁的,就將這個參數值設小點,更新不頻繁就設大點。
一般這個參數設置5s、10s、15s、20s、30s等等。

設置方法:
直接登陸從庫的mysql在線修改:

mysql> set global slave_net_timeout = 5;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show variables like 'slave_net_timeout';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| slave_net_timeout | 5     |
+-------------------+-------+
1 row in set (0.01 sec)

或者在從庫的myc.nf里添加:
[root@slave-server ~]# cat /usr/local/mysql/my.cnf
....
[mysqld]
.....
slave_net_timeout = 5
[root@slave-server ~]# /etc/init.d/mysql restart

因此,將這個參數設置恰當后,遇到上面問題的時候,從庫就會按照設定的時間去主動重新連接主庫同步數據,就不需要人工干預。

當然,上述場景是非常特殊的,一般出現的概率比較小,但是作為運維人員,我們非常有必要搞清楚該怎么應對這種情況。這就需要我們要更加深入的吃透MySQL replication重試機制。

接下來基於mysql主從復制原理來分析這一現象
MySQL的Replication是區別其他數據庫很關鍵的地方,也是可擴展性和高可用的基礎。它本身已經非常智能化,只需要我們調用Change Master指定Binlog 文件名和偏移位置就可以搭建從主庫到備庫的復制關系。
MySQL復制線程會自動將目前復制位置記錄下來,在主備復制中斷的時候自動連上主庫,並從上次中斷的位置重新開始復制。這些操作都是全自動化的,不需要人為的干預。這給了我們運維人員帶來了很多便利,同時也隱藏了很多細節。要真正的理解前面問題的真相以及怎么解決這個問題,我們還是需要真正的理解MySQL復制的原理。

1)Mysql主從復制的動作是“推”還是“拉”
MySQL的復制是“推”的,而不是“拉”的。
“拉”是指MySQL的備庫不斷的循環詢問主庫是否有數據更新,這種方式資源消耗多,並且效率低。
“推”是指MySQL的主庫在自己有數據更新的時候推送這個變更給備庫,這種方式只有在數據有變更的時候才會發生交互,資源消耗少。
顯而易見,“推”的方式更加符合程序運行的節能原則。

那么MySQL具體是怎么“推”的列呢?
實際上備庫在向主庫申請數據變更記錄的時候,需要指定從主庫Binlog的哪個文件(MASTER_LOG_FILE)的具體多少個字節偏移位置(MASTER_LOG_POS)。對應的,主庫會啟動一個Binlog dump的線程,將變更的記錄從這個位置開始一條一條的發給備庫。備庫一直監聽主庫過來的變更,接收到一條,才會在本地應用這個數據變更。

2)原因解析
從上面的分析,我們可以大致猜到為什么 show slave status 顯示一切正常,但是實際上主庫的變更都無法同步到備庫上來:
出現問題的時候,Binlog dump程序被kill掉了。而備庫作為監聽的一方,它一直沒有收到任何變更,它會認為主庫上長時間沒有任何變更,導致沒有變更數據推送過來。
備庫是無法判斷主庫上對應的Binlog dump線程到底是意外終止了,還是長時間沒有任何數據變更的。所以,對這兩種情況來說,備庫都顯示為正常。

所以該問題的關鍵在於:
主庫Binlog dump線程kill的消息由於網絡堵塞或者其他原因無法發送到備庫,而備庫卻認為主庫上的數據給有變更,因為雙方數據產生了差異。
而備庫只能在默認的3600s后主動地重新去連接主庫,屆時它才會發現主庫的數據有變動了,才會自動同步過來,這是需要等待很長時間。

3)問題避免
基於上面的分析,可以知道MySQL在這種情況下確實無法避免,那么有哪些辦法可以避開:
   1--被動處理:修改延遲的監控方法,發現問題及時處理。
   2--主動預防:正確設置--master-retry-count ,--master-connect-retry ,--slave-net-timeout 復制重試參數。

   1--被動處理
   MySQL的延遲監控大部分直接采集show slave status中的Seconds_Behind_Master 。
   那么像上面說的這種情況下, Seconds_Behind_Master就無法用來真實的衡量主備之間的復制延遲了。
   推薦使用Percona提供的監控方案(參考:mysql主從同步(3)-percona-toolkit工具(數據一致性監測、延遲監控)使用梳理

   2--主動預防
   除了手動在從庫上stop slave和start slave重新執行復制后,還需要指定三個參數,用於復制線程重連主庫,分別是
   master-retry-count:連接重試的次數。
   master-connect-retry:連接失敗后等待的秒數
   slave-net-timeout:上面已介紹
   其中 master-connect-retry 和 master-retry-count 需要在 Change Master 搭建主備復制時指定,而 slave-net-timeout 是一個全局變量,可以在 MySQL 運行時在線設置。
   不過要注意的是:master-connect-retry和master-retry-count參數在Mysql5.6版本里就被去除了,所以Mysql5.6版本及更高版本就只設置slave-net-timeout參數即可。

  具體的重試策略為:
  備庫過了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 設置的過小也有問題,這樣會導致如果主庫的變更確實比較少的時候,備庫頻繁的重新連接主庫,造成資源浪費。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM