深刻解析Mysql 主從同步延遲原理及解決方案

https://www.cnblogs.com/cnmenglang/p/6393769.htmlhtml

MySQL的主從同步是一個很成熟的架構,優勢爲:①在從服務器能夠執行查詢工做(即咱們常說的讀功能),下降主服務器壓力;②在從主服務器進行備份,避免備份期間影響主服務器服務;③當主服務器出現問題時,能夠切換到從服務器。mysql

相信你們對於這些好處已經很是瞭解了,在項目的部署中也採用這種方案。可是MySQL的主從同步一直有從庫延遲的問題,那麼爲何會有這種問題。這種問題如何解決呢?sql

1. MySQL數據庫主從同步延遲原理。數據庫

2. MySQL數據庫主從同步延遲是怎麼產生的。緩存

3. MySQL數據庫主從同步延遲解決方案。安全

 

1. MySQL數據庫主從同步延遲原理。服務器

答:談到MySQL數據庫主從同步延遲原理,得從mysql的數據庫主從複製原理提及,mysql的主從複製都是單線程的操做,主庫對全部DDL和 DML產生binlog,binlog是順序寫,因此效率很高,slave的Slave_IO_Running線程到主庫取日誌,效率很比較高,下一步, 問題來了,slave的Slave_SQL_Running線程將主庫的DDL和DML操做在slave實施。DML和DDL的IO操做是隨即的,不是順 序的,成本高不少,還可能可slave上的其餘查詢產生lock爭用,因爲Slave_SQL_Running也是單線程的,因此一個DDL卡主了,須要 執行10分鐘,那麼全部以後的DDL會等待這個DDL執行完纔會繼續執行,這就致使了延時。有朋友會問:「主庫上那個相同的DDL也須要執行10分,爲什 麼slave會延時?」,答案是master能夠併發,Slave_SQL_Running線程卻不能夠。網絡

2. MySQL數據庫主從同步延遲是怎麼產生的。多線程

答:當主庫的TPS併發較高時,產生的DDL數量超過slave一個sql線程所能承受的範圍,那麼延時就產生了,固然還有就是可能與slave的大型query語句產生了鎖等待。架構

3. MySQL數據庫主從同步延遲解決方案

答:最簡單的減小slave同步延時的方案就是在架構上作優化,儘可能讓主庫的DDL快速執行。還有就是主庫是寫,對數據安全性較高,好比 sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之類的設置,而slave則不須要這麼高的數據安全,徹底能夠講sync_binlog設置爲0或者關閉binlog,innodb_flushlog也 能夠設置爲0來提升sql的執行效率。另外就是使用比主庫更好的硬件設備做爲slave。

附加:

sync_binlog 配置說明:

sync_binlog」:這個參數是對於MySQL系統來講是相當重要的,他不只影響到Binlog對MySQL所帶來的性能損耗,並且還影響到MySQL中數據的完整性。對於「sync_binlog」參數的各類設置的說明以下:

sync_binlog=0,當事務提交以後,MySQL不作fsync之類的磁盤同步指令刷新binlog_cache中的信息到磁盤,而讓Filesystem自行決定何時來作同步,或者cache滿了以後才同步到磁盤。

sync_binlog=n,當每進行n次事務提交以後,MySQL將進行一次fsync之類的磁盤同步指令來將binlog_cache中的數據強制寫入磁盤。

在MySQL中系統默認的設置是sync_binlog=0,也就是不作任何強制性的磁盤刷新指令,這時候的性能是最好的,可是風險也是最大的。由於一旦系統Crash,在binlog_cache中的全部binlog信息都會被丟失。而當設置爲「1」的時候,是最安全可是性能損耗最大的設置。由於當設置爲1的時候,即便系統Crash,也最多丟失binlog_cache中未完成的一個事務,對實際數據沒有任何實質性影響。

從以往經驗和相關測試來看,對於高併發事務的系統來講,「sync_binlog」設置爲0和設置爲1的系統寫入性能差距可能高達5倍甚至更多。

innodb_flush_log_at_trx_commit 配置說明:

默認值1的意思是每一次事務提交或事務外的指令都須要把日誌寫入(flush)硬盤,這是很費時的。特別是使用電 池供電緩存(Battery backed up cache)時。設成2對於不少運用,特別是從MyISAM錶轉過來的是能夠的,它的意思是不寫入硬盤而是寫入系統緩存。日誌仍然會每秒flush到硬 盤,因此你通常不會丟失超過1-2秒的更新。設成0會更快一點,但安全方面比較差,即便MySQL掛了也可能會丟失事務的數據。而值2只會在整個操做系統 掛了時纔可能丟數據。 

 

 

mysql-5.6.3已經支持了多線程的主從複製。原理和丁奇的相似,丁奇的是以表作多線程,Oracle使用的是以數據庫(schema)爲單位作多線程,不一樣的庫可使用不一樣的複製線程。

 

基於局域網的master/slave機制在一般狀況下已經能夠知足'實時'備份的要求了。若是延遲比較大,就先確認如下幾個因素: 
1. 網絡延遲
2. master負載
3. slave負載
通常的作法是,使用多臺slave來分攤讀請求,再從這些slave中取一臺專用的服務器,只做爲備份用,不進行其餘任何操做,就能相對最大限度地達到'實時'的要求了

slave_net_timeout單位爲秒 默認設置爲 3600秒

參數含義:當slave從主數據庫讀取log數據失敗後,等待多久從新創建鏈接並獲取數據

master-connect-retry單位爲秒 默認設置爲 60秒

參數含義:當從新創建主從鏈接時,若是鏈接創建失敗,間隔多久後重試。

一般配置以上2個參數能夠減小網絡問題致使的主從數據同步延遲

 

判斷主從延時,一般有兩個方法:

1. Seconds_Behind_Master  vs  2. mk-heartbeat,下面具體說下二者在實現功能的差異。

能夠經過監控show slave status\G命令輸出的Seconds_Behind_Master參數的值來判斷,是否有發生主從延時。
其值有這麼幾種:
NULL - 表示io_thread或是sql_thread有任何一個發生故障,也就是該線程的Running狀態是No,而非Yes.
0 - 該值爲零,是咱們極爲渴望看到的狀況,表示主從複製良好,能夠認爲lag不存在。
正值 - 表示主從已經出現延時,數字越大表示從庫落後主庫越多。
負值 - 幾乎不多見,只是聽一些資深的DBA說見過,其實,這是一個BUG值,該參數是不支持負值的,也就是不該該出現。

Seconds_Behind_Master是經過比較sql_thread執行的event的timestamp和io_thread複製好的 event的timestamp(簡寫爲ts)進行比較,而獲得的這麼一個差值。咱們都知道的relay-log和主庫的bin-log裏面的內容徹底一 樣,在記錄sql語句的同時會被記錄上當時的ts,因此比較參考的值來自於binlog,其實主從沒有必要與NTP進行同步,也就是說無需保證主從時鐘的 一致。你也會發現,其實比較真正是發生在io_thread與sql_thread之間,而io_thread才真正與主庫有關聯,因而,問題就出來了, 當主庫I/O負載很大或是網絡阻塞,io_thread不能及時複製binlog(沒有中斷,也在複製),而sql_thread一直都能跟上 io_thread的腳本,這時Seconds_Behind_Master的值是0,也就是咱們認爲的無延時,可是,實際上不是,你懂得。這也就是爲什 麼你們要批判用這個參數來監控數據庫是否發生延時不許的緣由,可是這個值並非老是不許,若是當io_thread與master網絡很好的狀況下,那麼 該值也是頗有價值的。(就比如:媽–兒子–媳婦的關係,媽與兒子親人,媳婦和兒子也親人,不見得媳婦與媽就很親。開個玩笑:-)以前,提到 Seconds_Behind_Master這個參數會有負值出現,咱們已經知道該值是io_thread的最近跟新的ts與sql_thread執行到 的ts差值,前者始終是大於後者的,惟一的肯能就是某個event的ts發生了錯誤,比以前的小了,那麼當這種狀況發生時,負值出現就成爲可能。

方法2. mk-heartbeat,Maatkit萬能工具包中的一個工具,被認爲能夠準確判斷複製延時的方法。

mk-heartbeat的實現也是藉助timestmp的比較實現的,它首先須要保證主從服務器必需要保持一致,經過與相同的一個NTP server同步時鐘。它須要在主庫上建立一個heartbeat的表,裏面至少有id與ts兩個字段,id爲server_id,ts就是當前的時間戳 now(),該結構也會被複制到從庫上,表建好之後,會在主庫上之後臺進程的模式去執行一行更新操做的命令,按期去向表中的插入數據,這個週期默認爲1 秒,同時從庫也會在後臺執行一個監控命令,與主庫保持一致的週期去比較,複製過來記錄的ts值與主庫上的同一條ts值,差值爲0表示無延時,差值越大表示 延時的秒數越多。咱們都知道複製是異步的ts不願徹底一致,因此該工具容許半秒的差距,在這以內的差別均可忽略認爲無延時。這個工具就是經過實打實的復 制,巧妙的借用timestamp來檢查延時,贊一個!

 

===============================================

 

 

因爲歷史緣由,MySQL複製基於邏輯的二進制日誌,而非重作日誌。屢次被問到什麼時候MySQL能支持基於物理的複製,其實這就看MySQL各位大佬的想法。上次和賴老師腦暴,倏地說道:MySQL會不會來個基於Paxos的redo複製?

物理複製的真正好處不在於正確性,由於基於ROW格式的日誌複製也已能徹底保證複製的正確性。因爲物理日誌的寫入是在事務執行過程當中就不斷寫入,而二進制日誌的寫入僅僅在事務提交時。所以物理日誌的優點以下所示:

  • 複製架構下,大事務日誌提交速度快;
  • 複製架構下,主從數據延遲小;

假設執行了1個小時的某大事務,在最後提交時,只需寫入最後提交部分的重作日誌(redo log可視爲物理日誌)。雖然此大事務重作日誌寫入的總量可能有1G,然而在提交時,數據主從複製僅需將最後一部分日誌傳輸到遠程從機,由於以前的重作日誌已經在執行的1個小時內不斷地同步到從機。

對於二進制日誌,因爲其寫入時間發生在事務提交時,所以假設產生了1G的二進制日誌,則須要事務提交時間會包含這1G日誌的寫入時間。在Oracle中有一種說法,事務的提交速度都是平的,不論事務的大小。這在MySQL數據庫中是不成立的。即,MySQL的提交速度取決於事務產生的二進制日誌的大小,事務提交的速度不是平的。

更爲糟糕的是,MySQL主從複製在大事務下的延遲。一樣假設1個大事務在主服務器上執行了1個小時,則須要在最後的提交時間傳送到從服務器。主從延遲的時間至少爲1個小時,若從服務器執行還需1個小時,則主從複製延遲的最壞狀況多是2個小時。物理複製則不存在這樣的限制,緣由仍是如前所述,事務提交過程當中,日誌已經在傳輸和回放。

物理複製雖好,可是也有本身的缺陷,就我本身的實際體驗來看:

  • 物理複製下,主機壞塊會致使主從服務器都沒法啓動;相信遇到過此問題的同窗不在少數;
  • 此外,作ETL是有困難的,好比怎麼將物理日誌同步到Hadoop大數據平臺呢?

一言以蔽之,對於MySQL數據庫來講,任什麼時候刻不容許有大事務執行。若要執行,則將大事務拆成一個個小的子事務來執行。這是最基本心法口訣,但卻又和Oracle有着很大不一樣。總之,氣宗、劍宗,本無好壞,學會理解其中的差別,融會貫通方可達風清揚般的致臻境界。

mysql 用主從同步的方法進行讀寫分離,減輕主服務器的壓力的作法如今在業內作的很是廣泛。 主從同步基本上能作到實時同步。我從別的網站借用了主從同步的原理圖。

在配置好了, 主從同步之後, 主服務器會把更新語句寫入binlog,   從服務器的IO 線程(這裏要注意, 5.6.3 以前的IO線程僅有一個,5.6.3以後的有多線程去讀了,速度天然也就加快了)回去讀取主服務器的binlog 而且寫到從服務器的Relay log 裏面,而後從服務器的 的SQL thread 會一個一個執行 relay log 裏面的sql , 進行數據恢復。

relay 就是 傳遞, relay race 就是接力賽的意思

1. 主從同步的延遲的緣由

咱們知道, 一個服務器開放N個連接給客戶端來鏈接的, 這樣有會有大併發的更新操做, 可是從服務器的裏面讀取binlog 的線程僅有一個, 當某個SQL在從服務器上執行的時間稍長 或者因爲某個SQL要進行鎖表就會致使,主服務器的SQL大量積壓,未被同步到從服務器裏。這就致使了主從不一致, 也就是主從延遲。

2. 主從同步延遲的解決辦法

實際上主從同步延遲根本沒有什麼一招制敵的辦法, 由於全部的SQL必須都要在從服務器裏面執行一遍,可是主服務器若是不斷的有更新操做源源不斷的寫入, 那麼一旦有延遲產生, 那麼延遲加劇的可能性就會原來越大。 固然咱們能夠作一些緩解的措施。

  • a. 咱們知道由於主服務器要負責更新操做, 他對安全性的要求比從服務器高, 全部有些設置能夠修改,好比sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之類的設置,而slave則不須要這麼高的數據安全,徹底能夠講sync_binlog設置爲0或者關閉binlog,innodb_flushlog, innodb_flush_log_at_trx_commit 也 能夠設置爲0來提升sql的執行效率 這個能很大程度上提升效率。另外就是使用比主庫更好的硬件設備做爲slave。
  • b. 就是把,一臺從服務器當度做爲備份使用, 而不提供查詢, 那邊他的負載下來了, 執行relay log 裏面的SQL效率天然就高了。
  • c. 增長從服務器嘍,這個目的仍是分散讀的壓力, 從而下降服務器負載。

3. 判斷主從延遲的方法

MySQL提供了從服務器狀態命令,能夠經過 show slave status 進行查看,  好比能夠看看Seconds_Behind_Master參數的值來判斷,是否有發生主從延時。

其值有這麼幾種:

NULL - 表示io_thread或是sql_thread有任何一個發生故障,也就是該線程的Running狀態是No,而非Yes.
0 - 該值爲零,是咱們極爲渴望看到的狀況,表示主從複製狀態正常

其它的方法我也沒試過, 暫時不作評論

總結

以上就是這篇文章的所有內容了,但願本文的內容對你們的學習或者工做具備必定的參考學習價值,謝謝你們對腳本之家的支持。若是你想了解更多相關內容請查看下面相關連接

相關文章
相關標籤/搜索