1. 程式人生 > >MySQL的主從複製延遲問題

MySQL的主從複製延遲問題

主從複製延遲產生的原因

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

解決方法:

    1.最簡單的減少slave同步延時的方案就是在架構上做優化,儘量讓主庫的DDL快速執行。還有就是主庫是寫,對資料安全性較高,比如sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之類的設定,而slave則不需要這麼高的資料安全,完全可以講sync_binlog設定為0或者關閉binlog,innodb_flushlog也可以設定為0來提高sql的執行效率。另外就是使用比主庫更好的硬體裝置作為slave。mysql-5.6.3已經支援了多執行緒的主從複製。原理和丁奇的類似,丁奇的是以表做多執行緒,

Oracle使用的是以資料庫(schema)為單位做多執行緒,不同的庫可以使用不同的複製執行緒。

sync_binlog=1

This makes MySQL synchronize the binary log’s contents to disk each time it commits a transaction

預設情況下,並不是每次寫入時都將binlog與硬碟同步。因此如果作業系統或機器(不僅僅是MySQL伺服器)崩潰,有可能binlog中最後的語句丟 失了。要想防止這種情況,你可以使用sync_binlog全域性變數(1是最安全的值,但也是最慢的),使binlog在每N次binlog寫入後與硬碟 同步。即使sync_binlog設定為1,出現崩潰時,也有可能表內容和binlog內容之間存在不一致性。如果使用InnoDB表,MySQL伺服器 處理COMMIT語句,它將整個事務寫入binlog並將事務提交到InnoDB中。如果在兩次操作之間出現崩潰,重啟時,事務被InnoDB回滾,但仍 然存在binlog中。可以用--innodb-safe-binlog選項來增加InnoDB表內容和binlog之間的一致性。(註釋:在MySQL 5.1中不需要--innodb-safe-binlog;由於引入了XA事務支援,該選項作廢了),該選項可以提供更大程度的安全,使每個事務的 binlog(sync_binlog =1)和(預設情況為真)InnoDB日誌與硬碟同步,該選項的效果是崩潰後重啟時,在滾回事務後,MySQL伺服器從binlog剪下回滾的 InnoDB事務。這樣可以確保binlog反饋InnoDB表的確切資料等,並使從伺服器保持與主伺服器保持同步(不接收 回滾的語句)。

innodb_flush_log_at_trx_commit (這個很管用)

抱怨Innodb比MyISAM慢 100倍?那麼你大概是忘了調整這個值。預設值1的意思是每一次事務提交或事務外的指令都需要把日誌寫入(flush)硬碟,這是很費時的。特別是使用電 池供電快取(Battery backed up cache)時。設成2對於很多運用,特別是從MyISAM錶轉過來的是可以的,它的意思是不寫入硬碟而是寫入系統快取。日誌仍然會每秒flush到硬 盤,所以你一般不會丟失超過1-2秒的更新。設成0會更快一點,但安全方面比較差,即使MySQL掛了也可能會丟失事務的資料。而值2只會在整個作業系統 掛了時才可能丟資料。

    2.MySQL從庫上有一個IO執行緒負責從主庫取binlog到寫到本地。另外有一個SQL執行緒負責執行這些本地日誌,實現命令重放;正常網路狀況下IO執行緒沒有效能問題(這個待會會用到),問題是SQL執行緒只有一個,更新速度跟不上。所以經常會看到從庫的CPU idle很高,但同步效能就是上不去。

原始效能 

單執行緒的SQL執行緒是造成這個問題的主要原因。比較直接的想法是把它改成多執行緒版本,這個據說官方版本開發中,其實我們也有一個這樣的patch,但是直接寫大片程式碼在線上提供服務的slave機器上這種事兒,都會因為擔心穩定性而很難推動(寫patch的和運維的同學,你們懂的)。

所以打算用一個“第三方”工具中轉,來實現多執行緒同步。基本結構如下:

 

   說明:

1、這些transefer從master上各自同步一部分的資料,分別獨立更新slave。多程序還是多執行緒均可。

2、Transfer與master之間非同步更新日誌,transfer與slve之間同步更新資料

3、從這可以看出這個方案的缺點之一:更新能夠被獨立分開。比較直觀的想法是,按照表分。

關於transfer

作為這個關鍵的轉發工具transfer,需要提供如下功能:

1、能夠指定同步master中的哪部分資料,並且能夠方便地修改這個配置以應對master的加表需求;

2、支援stop slave、start slave。支援快速切換到新主庫的change master命令。

3、能夠記錄讀取點,transfer自己重啟或master重啟後能夠按照記錄點繼續讀後面的binlog;

4、能夠記錄分發點,transfer自己重啟或slave重啟後能夠按照記錄點繼續同步給slave

Transfer的這麼多功能,自己造輪子就累了。這裡直接用MySQL來充當此角色。為了方便描述,下文還將之稱為transfer。Transfer更新slave在功能上可以使用federated引擎,但由於其糾結的實現導致效能上達不到要求,因此在MySQL框架層中作了一點修改――讀到同步日誌後,直接傳送給slave。

方案簡單描述如下:

    1、Slave機器上搭另外的若干個MySQL(transfer),將其設為Master的從庫,且設定replicate-do-table, 每個transfer承擔一部分的表。

   2、所有Transfer的更新目標都設定為slave,其更新方式是讀到日誌後直接mysql_real_query執行到slave上。

從這可以看出這個方案的缺點之二:只能支援statement格式的同步方式。其實row也能支援,後面再說。

在transfer放棄federated引擎改用直接傳送後,效能提升不少,從庫同步效能增加一倍,但從本文第一個圖的資料對比就知道,延遲還很大。

發現這個時候slave的機器cpu已經很忙了,idle 20%一下――這個算是好訊息,總比idle很高但效能上不去好。

實際上是因為每個transfer,雖然設定只同步其中的部分表,但在實現上是IO執行緒把master上的所有命令都備份到本地,然後在SQL執行緒執行的時候再判斷,若不符合replicate-do-table,再放棄。

這樣存在的問題,是n個transfer,磁碟寫了n倍,更嚴重的是導致SQL執行緒空轉。

我們上文提到整個流程中IO執行緒是比較空閒的,因此修改IO執行緒邏輯,在寫入磁碟前先判斷,若不符合本transfer的replicate-do-table設定,不寫盤,直接放棄。

方案效果

從庫的QPS由於執行緒切換會有抖動,但總的執行時間與主庫相同。從庫的cpu idle下降,與主庫幾乎同時恢復到100。

總結:

1、要求在slave機器上多配置n個transfer(是否在從庫上均可)

2、目前只能支援statement的binlog格式,實際上row可以支援,方案定了,開發計劃中。

3、跨表更新的語句,會按照其更新的第一個表,分發到唯一一個transfer,沒有重複更新的問題,但有時序性問題。

轉自:http://weadyweady.blog.51cto.com/3012956/1651169