MySQL默認的復制都是異步的,在服務器崩潰時丟失事務是使用異步復制不可避免的結果。而5.5之后推出的一項新功能:半同步復制,可以限制事務丟失的數量。
MySQL5.7在5.6/5.5的基礎上增強了幾點功能:
1)無數據丟失
MySQL5.6/5.5半同步復制的原理:提交事務的線程會被鎖定,直到至少一個Slave收到這個事務,由於事務在被提交到存儲引擎之后才被發送到Slave上,所以事務的丟失數量可以下降到最多每線程一個。因為事務是在被提交之后才發送給Slave的,當Slave沒有接收成功,並且Master掛了,會導致主從不一致:主有數據,從沒有數據。如下面的情況:(AFTER_COMMIT)
客戶端執行一個事務,master接收到之后提交后並把事務發送給slave,在發送的期間網絡出現波動,但要等待slave把binlog寫到本地的relay-log,然后給master一個返回信息,等待以rpl_semi_sync_master_timeout參數設置的超時為准(默認為10秒)響應。在這等待的10秒里,其他會話查可以看到Master上的事務,此時一旦master發生宕機,由於事務沒有發送給slave,而master已經提交了,導致數據不一致。 例子: A客戶端執行的事務將字段Z從0修改為1。 1.A提交事務到master 2.master寫binlog 3.master commit事務到存儲引擎,再把事務發送給slave 4.master commit成功了! 說明:此時還未收到slave確認,A還在等待slave的響應,但是另外客戶端B已經可以看到字段Z為1了。假如此時master崩潰,如果slave實際收到剛才的事務僅僅是master未收到確認,那么此時slave的數據還是正確的也是Z=1,客戶端切換到slave后,都看到Z=1,但是如果slave沒有實際收到剛才的事務,那么此時slave上的z=0,導致主從數據不一直。
MySQL5.7在Master事務提交的時間方面做了改進(rpl_semi_sync_master_wait_point:AFTER_COMMIT\AFTER_SYNC),事務是在提交之前發送給Slave(默認,after_sync),當Slave沒有接收成功,並且Master宕機了,不會導致主從不一致,因為此時主還沒有提交,所以主從都沒有數據。MySQL5.7也支持和MySQL5.5\5.6一樣的機制:事務提交之后再發給Slave(after_commit)。如下面的情況:(AFTER_SYNC)
客戶端執行一個事務,master接收到之后就把事務發送給slave,slave收到事務之后,然后給master一個返回信息,master再提交事務。在slave返回信息的時間里(以rpl_semi_sync_master_timeout參數為准,默認為10秒),其他會話查看不到Master上的最新事務,因為master都還沒提交事務,此時一旦master發生宕機,由於事務沒有發送給slave,並且master也沒有提交數據,主從數據都沒有更改,所以不會出現數據不一致。 例子: A客戶端執行的事務講字段Z從0修改為1。
1.A提交事務到master 2.master寫binlog
3.master發送事務給slave,不提交! 4.master等待slave確認 此時z=0,沒有任何客戶端能看到z=1的結果,因為master還沒提交。 5.master收到slave確認,master開始commit到存儲引擎 6.master commit成功了!master返回結果給客戶端 說明:假如第4步時master崩潰,客戶端切換到slave,如果slave接收到事務,並響應master,那么此時主從的z=1,如果slave未接收到事務和響應,那么此時z=0,無論哪種狀態,對於所有客戶端數據庫都是一致,事務都沒有丟失。
參數rpl_semi_sync_master_wait_point:該參數控制半同步復制在哪個點(提交后再等待響應還是響應后再提交)等待slave的響應,默認AFTER_SYNC(slave響應后再提交),可選值有AFTER_COMMIT(提交后再等待響應)。
after_commit:master把每一個事務寫到二進制日志並保存到磁盤上,並且提交(commit)事務,再把事務發送給從庫,開始等待slave的應答。響應后master返回結果給客戶端,客戶端才可繼續。 after_sync :master把每一個事務寫到二進制日志並保存磁盤上,並且把事務發送給從庫,開始等待slave的應答。確認slave響應后,再提交(commit)事務到存儲引擎,並返回結果給客戶端,客戶端才可繼續。
總之,MySQL5.7是在Master收到Slave應答之后才Commit事務,MySQL5.6/5.5是在Master Commit之后才等待Slave的應答。MySQL5.7半同步的好處就是在確認事務復制到Slave之前,並發的其他線程看不到當前事務的數據。當Master故障時,要么提交的事務已經復制到Slave,要么全部都沒提交,這樣就保證了數據的一致性,推薦閱讀MySQL 5.7 深度解析: 半同步復制技術。
2)更快的半同步復制。
MySQL5.5/5.6的半同步復制是一個單工通訊方式,master把事務發送完畢后,要接收和處理slave的應答,處理完應答之后才能繼續發送下一個事務,示意圖如下:

MySQL5.7的半同步復制創建了單獨的應答接收線程,變成了雙工模式,發送和接收互不影響。因為有了相應的線程處理,發送效率得到大幅提升,相比MySQL5.5/5.6延遲會小很多,性能得到大幅提升。示意圖如下:

注意:MySQL5.7單獨的應答接收線程在開啟半同步復制的時候默認就創建了,不需要額外的設置。
3)等待多個Slave應答。
在半同步復制中,Master發送事務默認至少有一個Slave得到響應才能繼續下一個事務。MySQL5.7之后用戶可以設置應答的Slave數量,並且可以通過參數rpl_semi_sync_master_wait_for_slave_count:該變量控制slave應答的數量,默認是1,表示master接收到幾個slave應答后才commit。在多從的環境下,設置大於1可以提高數據的可靠性。
如何建立半同步復制:可以看官方文檔或則之前寫的初識 MySQL 5.5、5.6 半同步復制
主上執行: mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; Query OK, 0 rows affected (0.07 sec) mysql> SET GLOBAL rpl_semi_sync_master_enabled=1; Query OK, 0 rows affected (0.00 sec) 為了保證重啟后繼續生效,需要在配置文件里加入:rpl_semi_sync_master_enabled = 1 從上執行: mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; Query OK, 0 rows affected (0.04 sec) mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1; Query OK, 0 rows affected (0.00 sec) 為了保證重啟后繼續生效,需要在配置文件里加入:rpl_semi_sync_slave_enabled = 1 開啟復制:設置好半同步復制的插件和開啟半同步功能之后,復制模式就默認用半同步了 mysql> CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_LOG_FILE='mysql-bin-3306.000001',MASTER_LOG_POS=154; Query OK, 0 rows affected, 2 warnings (0.30 sec) mysql> start slave; Query OK, 0 rows affected (0.01 sec) 開啟成功后,slave的error log里會出現:半同步復制是跟 IO_THREAD 有直接關系,跟 SQL_THREAD 沒關系。也就是說SLAVE 從庫接收完二進制日志后給 master 主庫一個確認,並不管relay-log中繼日志是否正確執行完。即使SQL線程報錯了,半同步復制還是不會切換成異步復制 [Note] Slave I/O thread: Start semi-sync replication to master 'repl@10.0.3.141:3306' in log 'mysql-bin-3306.000001' at position 154
如何監控半同步復制:可以看官方文檔或則之前寫的初識 MySQL 5.5、5.6 半同步復制
主上: mysql> show variables like 'rpl_semi%'; +-------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------+------------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 10000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_for_slave_count | 1 | | rpl_semi_sync_master_wait_no_slave | ON | | rpl_semi_sync_master_wait_point | AFTER_SYNC | +-------------------------------------------+------------+ 6 rows in set (0.00 sec) mysql> show global status like 'rpl_semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 0 | | Rpl_semi_sync_master_no_times | 0 | | Rpl_semi_sync_master_no_tx | 0 | | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 0 | | Rpl_semi_sync_master_tx_wait_time | 0 | | Rpl_semi_sync_master_tx_waits | 0 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 0 | +--------------------------------------------+-------+ 14 rows in set (0.00 sec) 從上: mysql> show variables like 'rpl_semi%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_semi_sync_slave_enabled | ON | | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+ 2 rows in set (0.00 sec) mysql> show global status like 'rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.00 sec)
半同步成功開啟之后,在主上show processlist可以看到:
Waiting for semi-sync ACK from slave;
針對上面的參數和變量說明:
主上: rpl_semi_sync_master_enabled:表示主上是否開啟半同步復制功能,可以動態修改。可選值:ON\OFF rpl_semi_sync_master_timeout:為了防止半同步復制中主在沒有收到S發出的確認發生堵塞,用來設置超時,超過這個時間值沒有收到信息,則切換到異步復制,執行操作。默認為10000毫秒,等於10秒,這個參數動態可調,表示主庫在某次事務中,如果等待時間超過10秒,那么則降級為異步復制模式,不再等待SLAVE從庫。如果主庫再次探測到,SLAVE從庫恢復了,則會自動再次回到半同步復制模式。可以設置成1000,即1秒。 rpl_semi_sync_master_wait_for_slave_count:控制slave應答的數量,默認是1,表示master接收到幾個slave應答后才commit。 rpl_semi_sync_master_wait_no_slave :當一個事務被提交,但是Master沒有Slave連接,這時M不可能收到任何確認信息,但M會在時間限制范圍內繼續等待。如果沒有Slave鏈接,會切換到異步復制。是否允許master每個事務提交后都要等待slave的接收確認信號。默認為on,每一個事務都會等待。如果為off,則slave追趕上后,也不會開啟半同步復制模式,需要手工開啟。 rpl_semi_sync_master_wait_point:該參數表示半同步復制的主在哪個點等待從的響應,默認AFTER_SYNC,在得到slave的應答后再commit,可選值AFTER_COMMIT。 從上: rpl_semi_sync_slave_enabled:表示從上是否開啟半同步復制功能,可以動態修改。可選值:ON\OFF
Rpl_semi_sync_master_clients :說明支持和注冊半同步復制的已連Slave數。 Rpl_semi_sync_master_net_avg_wait_time :master等待slave回復的平均等待時間,單位毫秒。 Rpl_semi_sync_master_net_wait_time :master總的等待時間。 Rpl_semi_sync_master_net_waits :master等待slave回復的的總的等待次數,即半同步復制的總次數,不管失敗還是成功,不算半同步失敗后的異步復制。 Rpl_semi_sync_master_no_times :master關閉半同步復制的次數。 Rpl_semi_sync_master_no_tx :master沒有收到slave的回復而提交的次數,可以理解為master等待超時的次數,即半同步模式不成功提交數量。 Rpl_semi_sync_master_status :ON是活動狀態(半同步),OFF是非活動狀態(異步),用於表示主服務器使用的是異步復制模式,還是半同步復制模式。 Rpl_semi_sync_slave_status :Slave上的半同步復制狀態,ON表示已經被啟用,OFF表示非活動狀態。 Rpl_semi_sync_master_tx_avg_wait_time :master花在每個事務上的平均等待時間。 Rpl_semi_sync_master_tx_wait_time :master總的等待時間。 Rpl_semi_sync_master_tx_waits :master等待成功的次數,即master沒有等待超時的次數,也就是成功提交的次數 Rpl_semi_sync_master_wait_pos_backtraverse :master提交后來的先到了,而先來的還沒有到的次數。 Rpl_semi_sync_master_wait_sessions :前有多少個session因為slave的回復而造成等待。 Rpl_semi_sync_master_yes_tx :master成功接收到slave的回復的次數,即半同步模式成功提交數量。
總之,關於半同步復制的測試說明可以看初識 MySQL 5.5、5.6 半同步復制這篇文章。半同步復制的好處:半同步復制可以有效的限制事務丟失的數量,更好的保證數據的安全和一致性;半同步復制的壞處:更新、插入、刪除的速度要比異步復制要慢,因為多了一個"從返回信息給主"的步驟。要是出現異常:網絡問題或則數據庫問題,半同步復制和異步復制就會來回切換,導致主庫的更新、插入、刪除操作會受到影響。
...

