1. 程式人生 > 程式設計 >Mysql主從複製原理及同步延遲問題

Mysql主從複製原理及同步延遲問題

主從複製解決的問題

  • 資料分佈:通過複製將資料分佈到不同地理位置
  • 負載均衡:讀寫分離以及將讀負載到多臺從庫
  • 備份:可作為實時備份
  • 高可用性:利用主主複製實現高可用

複製原理

複製的原理其實很簡單,僅分為以下三步:

  1. 在主庫上把資料更改記錄到二進位制日誌binary log中,具體是在每次準備提交事務完成資料更新前,主庫將資料更新的事件記錄到二進位制日誌中去,Mysql會按照事務提交的順序來記錄二進位制日誌的。日誌記錄好之後,主庫通知儲存引擎提交事務。

  2. 從庫會啟動一個IO執行緒,該執行緒會連線到主庫。而主庫上的binlog dump執行緒會去讀取主庫本地的binlog日誌檔案中的更新事件。發往從庫,從庫接收到日誌之後會將其記錄到本地的中繼日誌relay-log當中。

  3. 從庫中的SQL執行緒讀取中繼日誌relay-log中的事件,將其重放到從庫中。(在5.6版本之前SQL執行緒是單執行緒的,使得主從之間延遲更大)

兩種複製方式

日誌檔案中記錄的到底是什麼呢? mysql支援了兩種日誌格式,這兩種日誌格式也體現了各自的複製方式

基於語句複製:

基於語句的複製相當於邏輯複製,即二進位制日誌記錄了操作的語句,通過這些語句在從庫進行重放來實現複製。這種方式簡單,二進位制日誌佔用空間少,使得頻寬小傳輸效率較高。 但是基於語句的更新依賴於其他因素,比如插入資料時利用時間戳函式呼叫當前時間作為時間值也會出現問題,因為由於主從之間的延遲導致時間值不一致。儲存過程和觸發器也可能出現問題。所以在開發當中我們應該將邏輯儘量放在程式碼層,而不應放到mysql中,不易擴充套件。

基於行復制:

基於行的複製相當於物理複製,即二進位制日誌記錄了實際更新資料的每一行。這樣導致行復制的壓力比較大,因為日誌佔用空間較大,傳輸佔用頻寬也較高。但是比基於語句複製更加精確,可以遮蔽一些由於主庫從庫之間的差異導致的不一致。如剛才提到的時間戳函式。

二者對比:

  • 語句複製:

    • 傳輸效率高,減少延遲。
    • 在從庫更新不存在的記錄時,語句賦值不會失敗。而行復制會導致失敗,從而更早發現主從之間的不一致。
    • 設表裡有一百萬條資料,一條sql更新了所有表,基於語句的複製僅需要傳送一條sql,而基於行的複製需要傳送一百萬條更新記錄
  • 行復制:

    • 不需要執行查詢計劃。
    • 不知道執行的到底是什麼語句。
    • 例如一條更新使用者總積分的語句,需要統計使用者的所有積分再寫入使用者表。如果是基於語句複製的話,從庫需要再一次統計使用者的積分,而基於行復制就直接更新記錄,無需再統計使用者積分。
因為兩種方式各有優缺點,所以mysql在這兩種複製模式進行動態的切換。預設是語句。

配置要點

# 如果在雙主複製結構中沒有設定ID的話就會導致迴圈同步問題
server_id=1

# 即日誌中記錄的是語句還是行更新或者是混合
binlog_format=mixed

# 在進行n次事務提交以後,Mysql將執行一次fsync的磁碟同步指令。將緩衝區資料重新整理到磁碟。
# 為0的話由Mysql自己控制頻率。
sync_binlog=n

# 為0的話,log buffer將每秒一次地寫入log file中並且重新整理到磁碟。
# mysqld程式崩潰會丟失一秒內的所有事務。
# 為1的話,每次事務log buffer會寫入log file並重新整理到磁碟。(較為安全)
# 在崩潰的時候,僅會丟失一個事務。
# 為2的話,每次事務log buffer會寫入log file,但一秒一次重新整理到磁碟
innodb_flush_logs_at_trx_commit=0


# 阻止從庫崩潰後自動啟動複製,給一些時間來修復可能的問題,
# 崩潰後再自動複製可能會導致更多的問題。並且本身就是不一致的
skip_slave_start=1 


# 是否將從庫同步的事件也記錄到從庫自身的bin-log中
# 允許備庫將重放的事件也記錄到自身的二進位制日誌中去,可以將備庫當做另外一臺主庫的從庫
log_slave_update 

# 日誌過期刪除時間,延遲嚴重的話會導致日誌檔案佔用磁碟
expire_logs_days=7

複製程式碼

innodb_flush_logs_at_trx_commit的三個引數很容易弄混。以下是詳細的解析:

mysql先將日誌寫到log buffer緩衝區當中,再將log buffer緩衝區的資料寫到log file日誌檔案中,此時寫入的是記憶體中的log file,最終仍需作業系統將記憶體中的資料刷寫到磁碟上。

  • 引數0:mysql每秒都會將log buffer的資料寫入到log file中並且重新整理到磁碟。意味著mysql崩潰的時候將會丟失一秒內的所有事務。
  • 引數1:每次事務提交都會將log buffer寫入到log file並重新整理到磁碟。意味著在mysql崩潰的時候,僅會丟失一個事務。
  • 引數2:每次事務提交都會將log buffer寫入到log file但不同時寫入到磁碟,由mysql自行控制每秒將log file刷寫到磁碟上,當mysql崩潰的時候作業系統沒崩潰的時候,log_file中僅會丟失一個事務,作業系統仍會將log file刷寫到磁碟,而如果作業系統也崩潰或斷電的話,則會丟失一秒內的事務。

推薦使用:innodb_flush_logs_at_trx_commit=2以及sync_binlog=500效能會較快。innodb_flush_logs_at_trx_commit以及sync_binlog都為1的話,較為安全。

延遲問題

延遲的產生:

  • 當主庫的TPS併發較高時,由於主庫上面是多執行緒寫入的,而從庫的SQL執行緒是單執行緒的,導致從庫SQL可能會跟不上主庫的處理速度(生產者比消費者快,導致商品堆積)。

延遲的解決:

網路方面:將從庫分佈在相同區域網內或網路延遲較小的環境中。

硬體方面:從庫配置更好的硬體,提升隨機寫的效能。

配置方面:從庫配置sync_binlog=0,innodb_flush_log_at_trx_commit=2,logs-slave-updates=0,增大innodb_buffer_pool_size,讓更多操作在Mysql記憶體中完成,減少磁碟操作。或者升級Mysql5.7版本使用並行複製。

架構方面:比如在事務當中儘量對主庫讀寫,其他非事務中的讀在從庫。消除一部分延遲帶來的資料庫不一致。增加快取降低一些從庫的負載。

筆者個人心得,如有錯誤懇請網友評論指正。

轉自我的個人部落格 vc2x.com