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版本使用並行復制。

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

 


免責聲明!

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



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