程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> mysql半同步(semi-sync)源碼實現,mysqlsemi-sync

mysql半同步(semi-sync)源碼實現,mysqlsemi-sync

編輯:MySQL綜合教程

mysql半同步(semi-sync)源碼實現,mysqlsemi-sync


      mysql復制簡單介紹了mysql semi-sync的出現的原因,並說明了semi-sync如何保證不丟數據。這篇文章主要側重於semi-sync的實現,結合源碼將semi-sync的實現過程展現給大家。最新的semi-sync源碼可以參考官方5.7版本的實現,https://github.com/mysql/mysql-server。

打開semi-sync的正確姿勢
     默認情況下的mysql復制都是異步復制,mysql通過參數來控制semi-sync開關。具體而言,主庫上通過rpl_semi_sync_master_enabled參數控制,備庫上通過rpl_semi_sync_slave_enabled參數控制,打開這兩個參數後,mysql semi-sync的特性就打開了。注意對於備庫而言,為了保證半同步立即生效,需要重啟slave的IO線程。另外,還有一個比較重要的參數是rpl_semi_sync_master_timeout,這個參數用於控制master等待semi-slave ack報文的時間,單位是毫秒,默認是10000。master等待超時,則切換為普通的異步復制。

master:
set global rpl_semi_sync_master_enabled=1;
set global rpl_semi_sync_master_timeout=xxx;

slave:
stop slave io_thread;
set global rpl_semi_sync_slave_enabled=1;
start slave io_thread;

另外需要注意的是,打開了上述兩個參數只說明master-slave已經具備打開semi-sync的基本條件了,但復制是否依照半同步運行,還需要根據Rpl_semi_sync_master_status的狀態值確定。因為比如slave較master有很大延遲(超過rpl_semi_sync_master_timeout),那麼復制切換為普通復制。對於需要調試代碼的童鞋而言,rpl_semi_sync_master_trace_level和rpl_semi_sync_slave_trace_level非常重要,通過設置level取值,可以打印日志記錄半同步的詳細過程,方便定位問題。

semi-sync的實現
semi-sync說到底也是一種復制,只不過是在普通的復制基礎上,添加了一些步驟來實現。因此semi-sync並沒有改變復制的基本框架,我們的討論也從master,slave兩方面展開。
1.master(主庫)

主庫上面主要包含三個部分,(1).負責與slave-io線程對接的binlog dump線程,將binlog發送到slave,(2).主庫上寫事務的工作線程,(3).收取semi-slave報文的ack_receiver線程。

(1).binlog dump流程
主要執行邏輯在mysql_binlog_send函數中。

1.判斷slave是否是semi_slave,調用add_slave將semi-slave加入到ack_receiver線程的監聽隊列中。判斷的邏輯是slave對應的會話上是否設置了參數rpl_semi_sync_slave。

2.根據slave的請求偏移和binlog文件,從指定位點讀取binlog
3.根據文件和位點,撈binlog文件中的數據
4.調用updateSyncHeader設置數據包頭semi-sync標記
根據實時semi-sync運行狀態來確定是否設置(這個狀態由ack_receiver線程根據是否及時收到slave-ack報文設置)
5.調用my_net_write發送binlog
6.調用net_flush確認網絡包發送出去
如果當前所有產生的binlog已經處理完,需調用wait_for_update_bin_log等待binlog更新。

(2).半同步事務提交流程
mysql5.6以後提交采用組提交方式,主要分為三個階段,每個階段有一個隊列,增加semi-sync後,又增加了一個semi-sync階段。
1.flush階段:
隊列能保證寫binlog的順序與innodb-commit的順序一致。通過隊列,可以保證順序寫每個事務的binlog-cache,然後只進行一次write操作(flush_cache_to_file)。flush階段後,如果sync_binlog不是1,則通知master有新binlog產生;如果sync_binlog為1,則等待sync階段後,再通知dump線程有新binlog產生。這裡我理解是因為如果不為1,則可能沒有後續的sync階段,而操作系統緩存也有binlog數據,所以可以在flush階段後通知;而對於sync_binlog為1的情況,可以保證主庫的binlog先落地,永遠比備庫多。但如果sync_binlog不為1,比如1000,則主機異常情況下,則可能出現備庫的binlog比主庫還多的情況。根據sync_binlog的設置,確認是否要跳過sync階段。
2.sync階段:
sync_binlog_file
3.semi_sync階段:
call_after_sync,等待備庫應答。調用cond_timewait等待條件變量&COND_binlog_send_
4.commit階段:
innodb-commit
waitAfterCommit,等待備庫應答。調用cond_timewait等待條件變量&COND_binlog_send_ 。最終我們根據semi-sync復制模式的設置(AFTER_COMMIT,AFTER_SYNC),來確定是第(3)步還是第(4)步進行等待。

(3).接收slave-ack報文流程
這個流程的工作主要在ack_receiver線程中,這個線程的主要作用是監聽semi-slave的ack包,確認master-slave鏈路是否工作在半同步狀態,並根據實際運行狀態將普通復制與半同步復制進行切換。打開主庫rpl_semi_sync_master_enabled參數後,該線程啟動,關閉參數後,該線程消亡。
流程如下:
1.遍歷semi-slave數組
2.通過select函數監聽每個slave是否有網絡包過來
3.調用my_net_read讀取包數據
4.調用reportReplyPacket處理semi-sync復制狀態
若備庫已經獲取了最新的binlog位點,則喚醒等待的工作線程
5.調用reportReplyBinlog喚醒等待的線程,mysql_cond_broadcast(&COND_binlog_send_);

2.slave(備庫)

主要實現在(handle_slave_io)
1.啟動io-thread後,調用safe_connect建立與master的連接
2.調用request_dump函數處理請求binlog邏輯
(1).執行命令SET @rpl_semi_sync_slave= 1,設置一個局部變量,通過這個參數標記slave為semi-slave
(2).發送命令COM_BINLOG_DUMP請求日志
循環從master端收取日志,處理日志
{
  1.調用read_event,從master端收取日志(如果沒有網絡包,會阻塞等待)
  2.調用slaveReadSyncHeader,確定網絡包頭是否有semi-sync標記
  3.調用queue_event將日志寫入relay-log,在這個過程中會過濾自身server-id的日志
  4.如果有semi-sync標記,調用slaveReply函數,發送ack報文
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved