MySQL 8 複製(二)——半同步複製

lhrbest發表於2020-02-12

MySQL 8 複製(二)——半同步複製


目錄


 


一、簡介


二、效能提升


1. 支援傳送二進位制日誌事件和接收ACK的非同步化


2. 控制主庫接收確認反饋從庫的數量


3. 二進位制日誌互斥鎖改進


三、資料一致性


1. 原始碼剖析


2. rpl_semi_sync_master_wait_point的配置


3. sync_binlog的配置


4. sync_relay_log的配置


四、管理介面


五、在MySQL 8上安裝配置半同步複製


1. 安裝外掛


2. 啟用半同步複製


3. 監控半同步複製


六、測試


1. 正常提交事務


2. 回滾事務


3. rpl_semi_sync_master_wait_no_slave為ON時從庫數小於rpl_semi_sync_master_wait_for_slave_count


4. rpl_semi_sync_master_wait_no_slave為OFF時從庫數小於rpl_semi_sync_master_wait_for_slave_count


一、簡介

        直到目前的最新版本為止,MySQL預設依然使用非同步複製策略。簡單說所謂非同步複製,指的是主庫寫二進位制日誌、從庫的I/O執行緒讀主庫的二進位制日誌寫本地中繼日誌、從庫的SQL執行緒重放中繼日誌,這三步操作都是非同步進行的。如此選擇的主要理由是出於效能考慮,與同步複製相比,非同步複製顯然更快,同時能承載更高的吞吐量。但非同步複製的缺點同樣明顯,不能保證主從資料實時一致,也無法控制從庫的延遲時間,因此它不適於要求主從資料實時同步的場景。例如,為了分解讀寫壓力,同一程式寫主庫讀從庫,但要求讀到的資料與讀主庫的相同,非同步複製不滿足這種強資料一致性需求。非同步複製的另一個問題是可能會有資料丟失,例如主庫當機時,已經提交的事務可能還沒有傳到從庫上,如果此時強行主從切換,可能導致新主庫上的資料不完整。


        於是在MySQL在5.5中就順其自然地引入了半同步複製,可用作非同步複製的替代方案,它具有以下特性:


從庫在連線主庫時表明它是否支援半同步複製。

如果在主庫啟用了半同步複製,並且至少有一個支援半同步複製的從庫,則主庫上執行事務提交的執行緒將等待,直到至少一個半同步從庫確認已收到事務的所有事件(此時從庫會向主庫傳送ACK,Acknowledgement),或者直到發生超時。

只有在將事件寫入其中繼日誌並重新整理到磁碟後,從庫才會確認收到事務的事件,向主庫傳送ACK。

如果在沒有任何從庫確認事務的情況下發生超時,則主庫將退化為非同步複製。當至少有一個半同步從庫趕上時,主庫恢復半同步複製。

必須在主庫和從庫都啟用半同步複製,否則使用非同步複製。

        當主庫阻塞(等待來自從庫確認)時,它不會返回執行事務的會話。阻塞結束時,主庫返回到會話,然後該會話可以繼續執行其它語句。此時,事務已在主庫提交,並且至少一個從庫已確認其事件的接收。在繼續之前,主庫必須收到的確認從庫的數量可使用rpl_semi_sync_master_wait_for_slave_count系統變數進行配置,預設值為1。


        不只是事務提交時,事務回滾時主庫也會發生同樣的阻塞。MySQL同時支援多個資料庫引擎,當一個事務中既包含事務表又包含非事務表時,回滾即使對事務表沒有影響,二進位制日誌中也會記錄非事務表的事件,因為對非事務表的修改無法回滾並且必須傳送到從庫。


        在沒以START TRANSACTION或SET autocommit = 0開啟事務時,每個語句都自動隱式提交。使用半同步複製時,主庫上的這類語句就像顯式事務提交一樣。


        為了加深對半同步複製中“半”的理解,簡單將其與非同步和全同步複製進行比較:


非同步複製:主庫提交事務時,將事件寫入它的二進位制日誌,而從庫在準備就緒時請求它們。主庫無需等待從庫的ACK回覆,直接提交事務並返回客戶端。非同步複製不確保所有事件都能到達從庫,無法保證資料完整性。

全同步複製:當主庫提交事務時,所有從庫也將在主庫返回執行事務的會話之前提交事務。這樣做的缺點是完成事務可能會有很大延遲。

半同步複製:介於非同步和完全同步複製之間。主庫僅等待至少一個從庫接收並記錄事件。它不會等待所有從庫確認收到,並且從庫只需要確認接收,而不是事件已在從庫完全執行和提交。

        與非同步複製相比,半同步複製提供了改進的資料完整性,因為當提交成功返回時,已知資料至少存在於兩個位置。但半同步複製確實會對效能產生一些影響,因為需要等待從庫,提交速度會變慢,延遲至少是將提交傳送到從庫並等待從庫確認收到的TCP/IP往返時間。這意味著半同步複製最好在低延時的網路中使用。


二、效能提升

1. 支援傳送二進位制日誌事件和接收ACK的非同步化

        舊版本的半同步複製受限於Binlog Dump執行緒,原因是該執行緒承擔了兩份不同且又十分頻繁的任務:傳送二進位制日誌事件給從庫 ;接收從庫的ACK反饋資訊。這兩個任務是序列的,Binlog Dump執行緒必須等待從庫返回之後才會傳送下一個事件。Binlog Dump執行緒已然成為整個半同步複製效能的瓶頸。在高併發業務場景下,這樣的機制會影響資料庫整體的TPS。單一Binlog Dump執行緒傳送接收的工作流程如圖1所示。

MySQL 8 複製(二)——半同步複製


圖1 單一Binlog Dump執行緒傳送接收的工作流程

        為了解決上述問題,在5.7.4版本的半同步複製框架中,獨立出一個Ack Receiver執行緒 ,專門用於接收從庫返回的ACK請求,這將之前Binlog Dump執行緒的傳送和接收工作分為了兩個執行緒來處理。這樣主庫上有兩個執行緒獨立工作,可以同時傳送二進位制日誌事件到從庫,和接收從庫的ACK資訊。因此半同步複製得到了極大的效能提升。Binlog Dump執行緒與Ack Receiver執行緒工作流程如圖2所示。


MySQL 8 複製(二)——半同步複製

圖2 Binlog Dump執行緒與Ack Receiver執行緒工作流程

 


        Ack Receiver執行緒在主庫啟用半同步複製時建立,並在主庫禁用半同步複製時銷燬。它是自動建立和銷燬的,因此不受使用者控制。它的狀態資訊可以在performance_schema中查詢得到:


select name, type, processlist_state from performance_schema.threads where name like '%ack_receiver%';

+------------------------------+------------+--------------------------------------+

| name                         | type       | processlist_state                    |

+------------------------------+------------+--------------------------------------+

| thread/semisync/Ack_receiver | BACKGROUND | Waiting for semi-sync ACK from slave |

+------------------------------+------------+--------------------------------------+

        Ack receiver執行緒有以下三個狀態:


Waiting for semi-sync slave connection

Waiting for semi-sync ACK from slave

Reading semi-sync ACK from slave

        在MySQL 5.7.17之前,這個Ack Receiver執行緒採用了select機制來監聽從庫返回的結果,然而select機制監控的檔案控制程式碼只能是0-1024,當超過1024時,使用者在MySQL的錯誤日誌中或許會收到類似如下的報錯,更有甚者會導致MySQL發生當機。


semi-sync master failed on net_flush() before waiting for slave reply.

        MySQL 5.7.17版本開始,官方修復了這個bug,開始使用poll機制來替換原來的select機制,從而可以避免上面的問題。其實poll呼叫本質上和select沒有區別,只是在I/O控制程式碼數理論上沒有上限了,原因是它是基於連結串列來儲存的。


2. 控制主庫接收確認反饋從庫的數量

        MySQL 5.7新增了rpl_semi_sync_master_wait_for_slave_count系統變數,可以用來控制主庫接收多少個從庫寫事務成功反饋,給高可用架構切換提供了靈活性。如圖3所示,當該變數值為2時,主庫需等待兩個從庫的ACK。

MySQL 8 複製(二)——半同步複製


圖3 一主兩從的半同步複製

 


        使用這個功能,可以在不同機房部署主伺服器和兩個從伺服器,並配置半同步複製以將事務複製到至少兩個從庫,以便在多個伺服器一次性崩潰的情況下減少資料丟失的可能,從庫越多,資料越安全。


3. 二進位制日誌互斥鎖改進

        舊版本半同步複製在主庫提交二進位制日誌的寫會話和Binlog Dump執行緒讀取二進位制日誌的操作都會對二進位制日誌新增binlog lock互斥鎖,用於保護二進位制日誌的讀寫操作。使用此互斥鎖,二進位制日誌讀寫操作是安全的,但會導致二進位制日誌檔案的讀寫序列化。不僅Binlog Dump執行緒和使用者會話不能同時讀寫二進位制日誌,就連多個Binlog Dump執行緒本身也無法同時讀取。每當一個會話正在讀取或寫入二進位制日誌檔案時,所有其它會話都必須等待。如此順序讀寫可能是一個瓶頸,尤其是當讀寫操作很慢時。序列化讀寫二進位制日誌如圖4所示。

MySQL 8 複製(二)——半同步複製


圖4 序列化讀寫二進位制日誌

 


        MySQL 5.7.2對binlog lock進行了以下兩方面優化:


從Binlog Dump執行緒中移除binlog lock。

加入了安全邊際保證二進位制日誌的讀安全。

        二進位制日誌檔案看起來像一個僅追加的日誌檔案,可以安全地讀取沒有鎖定的二進位制事件。因此從Binlog Dump執行緒中刪除了binlog鎖。不使用binlog鎖,而是為活動binlog維護安全讀取邊界(最大位置)。Binlog Dump執行緒永遠不會讀取超過安全讀取邊界的位置。當邊界到達邊界時,它將等待邊界更新。使用者會話負責在追加了二進位制事件後更新安全讀取邊界。改進後的二進位制日誌讀寫如圖5所示。

MySQL 8 複製(二)——半同步複製


圖5 改進後的二進位制日誌讀寫

 


        從圖中可知:


讀取二進位制日誌事件時,Binlog Dump執行緒不會相互阻塞。

正在寫二進位制日誌事件的使用者會話不會阻止Binlog Dump執行緒。

讀取二進位制日誌事件的Binlog Dump不會阻塞使用者會話。

        因此,Binlog Dump執行緒和使用者會話都可以獲得更好的吞吐量,尤其是當有很多從庫時,這種改進非常顯著。


三、資料一致性

        這一部分節選自阿里系的一篇文章,原文地址http://mysql.taobao.org/monthly/2017/04/01/。文中從MySQL原始碼的層面分析了半同步複製的資料一致性。如果不看原始碼,很難搞清楚這個複雜的問題。


1. 原始碼剖析

        以下原始碼版本均為官方MySQL 5.7。MySQL semi-sync是以外掛方式引入,在plugin/semisync目錄下。這裡以semi-sync主要的函式呼叫為入口,學習semi-sync原始碼。


plugin/semisync/semisync_master.cc

403 /*******************************************************************************

404  *

405  * <ReplSemiSyncMaster> class: the basic code layer for sync-replication master.

406  * <ReplSemiSyncSlave>  class: the basic code layer for sync-replication slave.

407  *

408  * The most important functions during semi-syn replication listed:

409  *

410  * Master:

          //實際由Ack_receiver執行緒呼叫,處理semi-sync複製狀態,獲取備庫最新binlog位點,喚醒對應執行緒

411  *  . reportReplyBinlog():  called by the binlog dump thread when it receives

412  *                          the slave's status information.

          //根據semi-sync執行狀態設定資料包頭semi-sync標記

413  *  . updateSyncHeader():   based on transaction waiting information, decide

414  *                          whether to request the slave to reply.

          //儲存當前binlog 檔名和偏移量,更新當前最大的事務 binlog 位置

415  *  . writeTranxInBinlog(): called by the transaction thread when it finishes

416  *                          writing all transaction events in binlog.

          //實現客戶端同步等待邏輯

417  *  . commitTrx():          transaction thread wait for the slave reply.

418  *

419  * Slave:

          //確認網路包頭是否有semi-sync標記

420  *  . slaveReadSyncHeader(): read the semi-sync header from the master, get the

421  *                           sync status and get the payload for events.

          //給Master傳送ACK報文

422  *  . slaveReply():          reply to the master about the replication progress.

423  *

424  ******************************************************************************/

Ack_receiver執行緒,不斷遍歷slave,通過select監聽slave網路包,處理semi-sync複製狀態,喚醒等待執行緒。

plugin/semisync/semisync_master_ack_receiver.cc Ack_receiver::run()

->plugin/semisync/semisync_master.cc ReplSemiSyncMaster::reportReplyPacket

  ->plugin/semisync/semisync_master.cc ReplSemiSyncMaster::reportReplyBinlog

 

binlog Dump執行緒。如果slave是semi-slave,通過add_slave將slave新增到監聽佇列,在傳送網路包時根據semi-sync執行狀態設定包頭的semi-sync標記。

sql/rpl_binlog_sender.cc Binlog_sender::run()

->sql/rpl_binlog_sender.cc Binlog_sender::send_binlog

  ->sql/rpl_binlog_sender.cc Binlog_sender::send_events

    ->sql/rpl_binlog_sender.cc Binlog_sender::before_send_hook

      ->plugin/semisync/semisync_master_plugin.cc repl_semi_before_send_event

        ->plugin/semisync/semisync_master.cc ReplSemiSyncMaster::updateSyncHeader

 

事務提交階段,在flush binlog後,儲存當前binlog 檔名和偏移量,更新當前最大的事務 binlog 位置。

sql/binlog.cc MYSQL_BIN_LOG::ordered_commit

 ->plugin/semisync/semisync_master_plugin.cc repl_semi_report_binlog_update//after_flush

   ->plugin/semisync/semisync_master.cc repl_semisync.writeTranxInBinlog

 

事務提交階段,客戶端等待處理邏輯,分為after_sync和after_commit兩種情況

sql/binlog.cc MYSQL_BIN_LOG::ordered_commit

  ->sql/binlog.cc process_after_commit_stage_queue || call_after_sync_hook

    ->plugin/semisync/semisync_master_plugin.cc repl_semi_report_commit || repl_semi_report_binlog_sync

      ->plugin/semisync/semisync_master.cc ReplSemiSyncMaster::commitTrx

 

Slave IO執行緒,讀取資料後後檢查包頭是否有semi-sync標記。

sql/rpl_slave.cc handle_slave_io

  ->plugin/semisync/semisync_slave_plugin.cc repl_semi_slave_read_event

    ->plugin/semisync/semisync_slave.cc ReplSemiSyncSlave::slaveReadSyncHeader

 

Slave IO執行緒,在queue event後,在需要回復Master ACK報文的時候,回覆Master ACK報文。

sql/rpl_slave.cc handle_slave_io

  ->plugin/semisync/semisync_slave_plugin.cc repl_semi_slave_queue_event

    ->plugin/semisync/semisync_slave.cc ReplSemiSyncSlave::slaveReply

        首先半同步方式,主庫在等待備庫ack時候,如果超時會退化為非同步,這就可能導致資料丟失。在接下來分析中,先假設rpl_semi_sync_master_timeout足夠大,不會退化為非同步方式。這裡通過三個引數rpl_semi_sync_master_wait_point、sync_binlog、sync_relay_log的配置來對semi-sync做資料一致性的分析。


2. rpl_semi_sync_master_wait_point的配置

(1)原始碼剖析


plugin/semisync/semisync_master_plugin.cc

 

68 int repl_semi_report_binlog_sync(Binlog_storage_param *param,

69                                  const char *log_file,

70                                  my_off_t log_pos)

71 {

72   if (rpl_semi_sync_master_wait_point == WAIT_AFTER_SYNC)

73     return repl_semisync.commitTrx(log_file, log_pos);

74   return 0;

75 }

 

97 int repl_semi_report_commit(Trans_param *param)

   ...

102   if (rpl_semi_sync_master_wait_point == WAIT_AFTER_COMMIT &&

106     return repl_semisync.commitTrx(binlog_name, param->log_pos);

(2)配置為WAIT_AFTER_COMMIT


MySQL 8 複製(二)——半同步複製

圖6

 


        rpl_semi_sync_master_wait_point為WAIT_AFTER_COMMIT時,commitTrx的呼叫在engine層commit之後(在ordered_commit函式中process_after_commit_stage_queue呼叫),如圖6所示。即在等待Slave ACK時候,雖然沒有返回當前客戶端,但事務已經提交,其他客戶端會讀取到已提交事務。如果Slave端還沒有讀到該事務的events,同時主庫發生了crash,然後切換到備庫。那麼之前讀到的事務就不見了,出現了幻讀,如圖7所示。圖片引自Loss-less Semi-Synchronous Replication on MySQL 5.7.2。


        (補充一點。除了幻讀,這種場景還有一個問題是,如果客戶端會重試提交該事務到新的主上,當當機的主庫重新啟動後,以從庫的身份重新加入到該主從結構中。此時會發現,該事務在從庫中被提交了兩次,一次是之前作為主的時候,一次是被新主同步過來的,結果依然是主從資料不一致。)


MySQL 8 複製(二)——半同步複製

圖7

 


(2)配置為WAIT_AFTER_SYNC

        MySQL針對上述問題,在5.7.2引入了Loss-less Semi-Synchronous,在呼叫binlog sync之後,engine層commit之前等待Slave ACK。這樣只有在確認Slave收到事務events後,事務才會提交。在commit之前等待Slave ACK,同時可以堆積事務,利於group commit,有利於提升效能。如圖8所示,圖片引自Loss-less Semi-Synchronous Replication on MySQL 5.7.2。


MySQL 8 複製(二)——半同步複製

圖8

 


        其實上圖流程中存在著會導致主備資料不一致,使主備同步失敗的情形。見下面sync_binlog配置的分析。


3. sync_binlog的配置

(1)原始碼剖析


sql/binlog.cc ordered_commit

       //當sync_period(sync_binlog)為1時,在sync之後update binlog end pos

9002   update_binlog_end_pos_after_sync= (get_sync_period() == 1);

       ...

9021     if (!update_binlog_end_pos_after_sync)

           //更新binlog end position,dump執行緒會傳送更新後的events

9022       update_binlog_end_pos();

       ...

         //

9057     std::pair<bool, bool> result= sync_binlog_file(false);

       ...

9061   if (update_binlog_end_pos_after_sync)

9062   {

       ...

9068       update_binlog_end_pos(tmp_thd->get_trans_pos());

9069   }

 

sql/binlog.cc sync_binlog_file

8618 std::pair<bool, bool>

8619 MYSQL_BIN_LOG::sync_binlog_file(bool force)

8620 {

8621   bool synced= false;

8622   unsigned int sync_period= get_sync_period();//sync_binlog值

       //sync_period為0不做sync操作,其他值為達到sync呼叫次數後sync

8623   if (force || (sync_period && ++sync_counter >= sync_period))

8624   {

(2)配置分析

        當sync_binlog為0的時候,binlog sync磁碟由作業系統負責。當不為0的時候,其數值為定期sync磁碟的binlog commit group數。當sync_binlog值大於1的時候,sync binlog操作可能並沒有使binlog落盤。如果沒有落盤,事務在提交前,Master掉電,然後恢復,那麼這個時候該事務被回滾。但是Slave上可能已經收到了該事務的events並且執行,這個時候就會出現Slave事務比Master多的情況,主備同步會失敗。所以如果要保持主備一致,需要設定sync_binlog為1。


        WAIT_AFTER_SYNC和WAIT_AFTER_COMMIT兩圖中Send Events的位置,也可能導致主備資料不一致,出現同步失敗的情形。實際在rpl_semi_sync_master_wait_point分析的圖中是sync binlog大於1的情況。根據上面原始碼,流程如圖9所示。Master依次執行flush binlog,update binlog position,sync binlog。如果Master在update binlog position後,sync binlog前掉電,Master再次啟動後原事務就會被回滾。但可能出現Slave獲取到Events,這也會導致Slave資料比Master多,主備同步失敗。

MySQL 8 複製(二)——半同步複製


圖9

 


        由於上面的原因,sync_binlog設定為1的時候,MySQL會update binlog end pos after sync。流程如圖10所示。這時候,對於每一個事務都需要sync binlog,同時sync binlog和網路傳送events會是一個序列的過程,效能下降明顯。


MySQL 8 複製(二)——半同步複製

圖10

 


4. sync_relay_log的配置

(1)原始碼剖析


sql/rpl_slave.cc handle_slave_io

 

5764       if (queue_event(mi, event_buf, event_len))

           ...

5771       if (RUN_HOOK(binlog_relay_io, after_queue_event,

5772                    (thd, mi, event_buf, event_len, synced)))

 

after_queue_event

->plugin/semisync/semisync_slave_plugin.cc repl_semi_slave_queue_event

->plugin/semisync/semisync_slave.cc ReplSemiSyncSlave::slaveReply

 

queue_event

->sql/binlog.cc MYSQL_BIN_LOG::append_buffer(const char* buf, uint len, Master_info *mi)

->sql/binlog.cc after_append_to_relay_log(mi);

->sql/binlog.cc flush_and_sync(0)

->sql/binlog.cc sync_binlog_file(force)

(2)配置分析

        在Slave的IO執行緒中get_sync_period獲得的是sync_relay_log的值,與sync_binlog對sync控制一樣。當sync_relay_log不是1的時候,semisync返回給Master的position可能沒有sync到磁碟。在gtid_mode下,在保證前面兩個配置正確的情況下,sync_relay_log不是1的時候,僅發生Master或Slave的一次Crash並不會發生資料丟失或者主備同步失敗情況。如果發生Slave沒有sync relay log,Master端事務提交,客戶端觀察到事務提交,然後Slave端Crash。這樣Slave端就會丟失掉已經回覆Master ACK的事務events。




        但當Slave再次啟動,如果沒有來得及從Master端同步丟失的事務Events,Master就Crash。這個時候,使用者訪問Slave就會發現資料丟失。




        (我們可以發現當前原生的MySQL主從複製實現要同時滿足資料一致性、高可用和高效能,依然是力有不逮。)


四、管理介面

        這裡所說的半同步複製管理介面包含相關外掛和變數。

(1)兩個外掛,主庫端的semisync_master.so和從庫端的semisync_slave.so,實現半同步複製功能。

(2)系統變數控制外掛行為,例如:


rpl_semi_sync_master_enabled:控制是否在主庫上啟用半同步複製。要啟用或禁用外掛,將此變數分別設定為1或0,預設值為0(關閉)。

rpl_semi_sync_master_timeout:一個以毫秒為單位的值,用於控制主庫在超時並退化到非同步複製之前等待來自從庫確認提交的時間。預設值為10000(10秒)。

rpl_semi_sync_slave_enabled:與rpl_semi_sync_master_enabled類似,控制啟用從庫的外掛。

(3)狀態變數用來反映半同步複製的狀態資訊,例如:


Rpl_semi_sync_master_clients:半同步從庫的數量。

Rpl_semi_sync_master_status:半同步複製當前是否在主庫上執行。如果已啟用外掛且未發生提交確認,則該值為ON。如果未啟用外掛,或者由於提交確認超時,主伺服器已回退到非同步複製,則為OFF。

Rpl_semi_sync_master_no_tx:從庫未成功確認的事務數。

Rpl_semi_sync_master_yes_tx:從庫成功確認的事務數。

Rpl_semi_sync_slave_status:半同步複製當前是否在從庫上執行。如果外掛已啟用且從庫的I/O執行緒正在執行,則此值為ON,否則為OFF。

        僅當使用INSTALL PLUGIN安裝了相應的外掛時,系統和狀態變數才可用。


五、在MySQL 8上安裝配置半同步複製

        實驗環境:

        主機IP

        172.16.1.125(主)

        172.16.1.126(從)

        172.16.1.127(從)


        軟體環境

        OS:CentOS Linux release 7.2.1511 (Core)

        MySQL:MySQL Community Server 8.0.16

        glibc:glibc-2.17-105.el7.x86_64


       硬體環境

       三臺虛擬機器,每臺基本配置為:


雙核雙CPU,Intel(R) Xeon(R) CPU E5-2420 0 @ 1.90GHz

8G實體記憶體,8G Swap

100G物理硬碟

        三臺主機已經配置了一主兩從的非同步複製,參見“MySQL 8 複製(一)——非同步複製”。


1. 安裝外掛

        半同步複製是使用外掛實現的,因此必須將外掛安裝到MySQL伺服器中才能使用它們。安裝外掛後,可以通過與之關聯的系統變數來控制它。安裝外掛之前,這些系統變數是不可用的。要使用半同步複製,必須滿足以下要求:


安裝外掛需要MySQL伺服器支援動態載入。要驗證這一點,檢查have_dynamic_loading系統變數的值是否為YES。MySQL 8預設為YES。

已經啟動了非同步複製。

半同步不支援多源複製。

        安裝設定半同步複製,需要REPLICATION_SLAVE_ADMIN或SUPER許可權。


        MySQL發行版包括主、從端的半同步複製外掛檔案semisync_master.so和semisync_slave.so,預設位於MySQL安裝目錄下的lib/plugin目錄下,本例中為/usr/local/mysql/lib/plugin。也可以通過設定plugin_dir系統變數的值指定外掛目錄位置。執行下面的SQL語句載入外掛。


-- 在主

install plugin rpl_semi_sync_master soname 'semisync_master.so';

 

-- 在每個從

install plugin rpl_semi_sync_slave soname 'semisync_slave.so';

        如果嘗試安裝外掛導致Linux上出現類似於此處所示的錯誤,則必須安裝libimf:


mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';

ERROR 1126 (HY000): Can't open shared library

'/usr/local/mysql/lib/plugin/semisync_master.so'

(errno: 22 libimf.so: cannot open shared object file:

No such file or directory)

        可以從https://dev.mysql.com/downloads/os-linux.html獲取libimf。        檢查INFORMATION_SCHEMA.PLUGINS表或使用SHOW PLUGINS語句驗證外掛安裝,例如:


mysql> select plugin_name, plugin_status

    ->   from information_schema.plugins

    ->  where plugin_name like '%semi%';

+----------------------+---------------+

| plugin_name          | plugin_status |

+----------------------+---------------+

| rpl_semi_sync_master | ACTIVE        |

+----------------------+---------------+

1 row in set (0.00 sec)

        檢查相關係統變數的初始值:


-- 主

mysql> show variables like '%semi%';

+-------------------------------------------+------------+

| Variable_name                             | Value      |

+-------------------------------------------+------------+

| rpl_semi_sync_master_enabled              | OFF        |

| 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 variables like '%semi%';

+---------------------------------+-------+

| Variable_name                   | Value |

+---------------------------------+-------+

| rpl_semi_sync_slave_enabled     | OFF   |

| rpl_semi_sync_slave_trace_level | 32    |

+---------------------------------+-------+

2 rows in set (0.00 sec)

        主相關係統變數:


rpl_semi_sync_master_enabled:主庫是否啟用了半同步複製,預設為OFF。

rpl_semi_sync_master_timeout:等待從庫的ACK回覆的超時時間,預設為10秒。

rpl_semi_sync_master_trace_level:半同步複製時主庫的除錯級別。

rpl_semi_sync_master_wait_for_slave_count:主庫在超時時間內需要收到多少個ACK回覆才認為此次提交成功,否則就降級為非同步複製。該變數在MySQL 5.7.3才提供,在此之前的版本都預設為收到1個ACK則確認成功,且不可更改。MySQL 5.7.3之後該變數的預設值也是1。

rpl_semi_sync_master_wait_no_slave:預設值為ON,當狀態變數Rpl_semi_sync_master_clients中的值小於rpl_semi_sync_master_wait_for_slave_count時,Rpl_semi_sync_master_status依舊為ON,只有當事務提交後等待rpl_semi_sync_master_timeout超時後,Rpl_semi_sync_master_status才會變為OFF,即降級為非同步複製;為OFF時,當狀態變數Rpl_semi_sync_master_clients中的值小於rpl_semi_sync_master_wait_for_slave_count時,Rpl_semi_sync_master_status立即顯示為OFF,即立即降級為非同步複製。

rpl_semi_sync_master_wait_point:控制主庫上commit、接收ack、返回訊息給客戶端的時間點。值為 AFTER_SYNC 或 AFTER_COMMIT。該選項是MySQL5.7.2後引入的,預設值為 AFTER_SYNC。此版本之前,等價於使用了 AFTER_COMMIT 模式。

        從相關係統變數:


rpl_semi_sync_slave_enabled:從庫是否開啟半同步複製。

rpl_semi_sync_slave_trace_level:從庫的除錯級別。

2. 啟用半同步複製

        安裝半同步複製外掛後,預設情況下會禁用它。必須在主庫和從庫都啟用外掛才能啟用半同步複製。如果僅啟用一側,則複製將是非同步的。(1)啟用半同步複製


-- 主

set global rpl_semi_sync_master_enabled = 1;

 

-- 從

set global rpl_semi_sync_slave_enabled = 1;

        以上的啟動方式是在命令列操作,也可寫在配置檔案中。


# 主

plugin-load="rpl_semi_sync_master=semisync_master.so"

rpl_semi_sync_master_enabled=1

 

# 從

plugin-load="rpl_semi_sync_slave=semisync_slave.so"

rpl_semi_sync_slave_enabled=1

        在有的高可用架構下,master和slave需同時啟動,以便在切換後能繼續使用半同步複製。


plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"

rpl-semi-sync-master-enabled = 1

rpl-semi-sync-slave-enabled = 1

(2)重啟從庫上的I/O執行緒


mysql> stop slave io_thread;

mysql> start slave io_thread;

        如果沒有重啟,則預設還是非同步複製。重啟後,從庫會在主庫上註冊為半同步複製的從庫角色。

(3)檢視半同步是否在執行


-- 主

mysql> show status like 'Rpl_semi_sync_master_status';

+-----------------------------+-------+

| Variable_name               | Value |

+-----------------------------+-------+

| Rpl_semi_sync_master_status | ON    |

+-----------------------------+-------+

1 row in set (0.01 sec)

 

-- 從

mysql> show status like 'Rpl_semi_sync_slave_status';

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| Rpl_semi_sync_slave_status | ON    |

+----------------------------+-------+

1 row in set (0.00 sec)

        這兩個變數常用來監控主從是否執行在半同步複製模式下。至此,MySQL半同步複製搭建完畢。


3. 監控半同步複製

        半同步複製功能的外掛公開了幾個狀態變數,可以檢查這些變數以確定其操作狀態。這些變數僅當安裝了半同步複製外掛以後才可用。


-- 主

mysql> show status like 'rpl_semi_sync%';

+--------------------------------------------+-------+

| Variable_name                              | Value |

+--------------------------------------------+-------+

| Rpl_semi_sync_master_clients               | 2     |

| 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 status like 'rpl_semi_sync%';

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| Rpl_semi_sync_slave_status | ON    |

+----------------------------+-------+

1 row in set (0.01 sec)

        從庫上只有一個半同步複製相關的狀態變數Rpl_semi_sync_slave_status,為ON時表示從庫使用半同步複製,OFF表示從庫使用非同步複製。


        主庫有14個半同步複製相關的狀態變數:


Rpl_semi_sync_master_clients:當前連線了多少個半同步從庫。

Rpl_semi_sync_master_net_avg_wait_time:主庫等待從庫回覆的平均時間,以微秒為單位。此變數始終為0,不推薦使用,並且將在以後的版本中刪除。

Rpl_semi_sync_master_net_wait_time:主庫等待從庫回覆的總時間,以微秒為單位。此變數始終為0,不推薦使用,並且將在以後的版本中刪除。

Rpl_semi_sync_master_net_waits:主庫等待從庫回覆的總次數。

Rpl_semi_sync_master_no_times:主庫關閉半同步複製的次數。

Rpl_semi_sync_master_no_tx:從庫未成功確認的事務數。

Rpl_semi_sync_master_status:為ON時表示主庫使用半同步複製,OFF表示主庫使用非同步複製。

Rpl_semi_sync_master_timefunc_failures:呼叫gettimeofday等時間函式時主庫失敗的次數。

Rpl_semi_sync_master_tx_avg_wait_time:主庫等待一個事務的平均時間,以微秒為單位。

Rpl_semi_sync_master_tx_wait_time:主等待事務的總時間,以微秒為單位。

Rpl_semi_sync_master_tx_waits:主庫等待事務的總次數。

Rpl_semi_sync_master_wait_pos_backtraverse:主庫等待事件的二進位制座標低於之前等待事件的總次數。當事務開始等待回覆的順序與其二進位制日誌事件的寫入順序不同時,就會發生這種情況。

Rpl_semi_sync_master_wait_sessions:當前等待從庫回覆的會話數。

Rpl_semi_sync_master_yes_tx:從庫成功確認的事務數。

六、測試

1. 正常提交事務

-- 主

mysql> create database test;

Query OK, 1 row affected (0.00 sec)

 

mysql> use test;

Database changed

mysql> create table test.t1 (a int) engine=innodb;

Query OK, 0 rows affected (0.02 sec)

 

mysql> insert into t1 values(1);

Query OK, 1 row affected (0.02 sec)

 

mysql> show status like 'rpl_semi_sync%';

+--------------------------------------------+-------+

| Variable_name                              | Value |

+--------------------------------------------+-------+

| Rpl_semi_sync_master_clients               | 2     |

| Rpl_semi_sync_master_net_avg_wait_time     | 0     |

| Rpl_semi_sync_master_net_wait_time         | 0     |

| Rpl_semi_sync_master_net_waits             | 6     |

| 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      | 758   |

| Rpl_semi_sync_master_tx_wait_time          | 2274  |

| Rpl_semi_sync_master_tx_waits              | 3     |

| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |

| Rpl_semi_sync_master_wait_sessions         | 0     |

| Rpl_semi_sync_master_yes_tx                | 3     |

+--------------------------------------------+-------+

14 rows in set (0.00 sec)

mysql>

-- 從

mysql> select * from test.t1;

+------+

| a    |

+------+

|    1 |

+------+

1 row in set (0.00 sec)

        MySQL 8預設是每條語句自動提交的。主庫等待3個事務的確認,分別對應create database、create table、insert語句。平均每個事務等待758微妙,從庫正常確認了3個事務。


2. 回滾事務

-- 主

mysql> show variables like 'autocommit';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| autocommit    | OFF   |

+---------------+-------+

1 row in set (0.01 sec)

 

mysql> set session autocommit=0;                  -- 關閉自動提交,開啟事務

Query OK, 0 rows affected (0.00 sec)

 

mysql> insert into t1 values(2);                  -- 向事務表插入記錄

Query OK, 1 row affected (0.00 sec)

 

mysql> create table t2 (a int) engine=myisam;     -- 執行一個DDL語句,建立非事務表t2

Query OK, 0 rows affected (0.01 sec)

 

mysql> insert into t1 values(3);                  -- 向事務表插入記錄

Query OK, 1 row affected (0.00 sec)

 

mysql> insert into t2 values(3);                  -- 向非事務表插入記錄

Query OK, 1 row affected (0.01 sec)

 

mysql> rollback;                                  -- 回滾事務

Query OK, 0 rows affected, 1 warning (0.00 sec)

 

mysql> show warnings;

+---------+------+---------------------------------------------------------------+

| Level   | Code | Message                                                       |

+---------+------+---------------------------------------------------------------+

| Warning | 1196 | Some non-transactional changed tables couldn't be rolled back |

+---------+------+---------------------------------------------------------------+

1 row in set (0.00 sec)

 

mysql> select * from t1;

+------+

| a    |

+------+

|    1 |

|    2 |

+------+

2 rows in set (0.00 sec)

 

mysql> select * from t2;

+------+

| a    |

+------+

|    3 |

+------+

1 row in set (0.00 sec)

 

mysql> show status like 'rpl_semi_sync%';

+--------------------------------------------+-------+

| Variable_name                              | Value |

+--------------------------------------------+-------+

| Rpl_semi_sync_master_clients               | 2     |

| Rpl_semi_sync_master_net_avg_wait_time     | 0     |

| Rpl_semi_sync_master_net_wait_time         | 0     |

| Rpl_semi_sync_master_net_waits             | 12    |

| 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      | 691   |

| Rpl_semi_sync_master_tx_wait_time          | 4149  |

| Rpl_semi_sync_master_tx_waits              | 6     |

| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |

| Rpl_semi_sync_master_wait_sessions         | 0     |

| Rpl_semi_sync_master_yes_tx                | 6     |

+--------------------------------------------+-------+

14 rows in set (0.00 sec)

mysql> 

-- 從

mysql> show variables like 'autocommit';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| autocommit    | ON    |

+---------------+-------+

1 row in set (0.01 sec)

mysql> melect * from test.t1;

+------+

| a    |

+------+

|    1 |

|    2 |

+------+

2 rows in set (0.00 sec)

mysql> select * from test.t2;

+------+

| a    |

+------+

|    3 |

+------+

1 row in set (0.01 sec)

mysql>

        從上面的測試可以得出以下結論:


set session autocommit=0語句不被複制。

DDL語句會觸發一個commit,自動提交DDL語句本身及其前面未提交的事務。

非事務表不能回滾。

Rpl_semi_sync_master_tx_waits和Rpl_semi_sync_master_yes_tx都增加了3,分別對應insert into t1 values(2);create table t2 (a int) engine=myisam;insert into t2 values(3);三個事務。

3. rpl_semi_sync_master_wait_no_slave為ON時從庫數小於rpl_semi_sync_master_wait_for_slave_count

-- 關閉兩個從庫的複製

mysql> stop slave;

Query OK, 0 rows affected (0.01 sec)

 

mysql> show status like 'rpl_semi_sync%';                -- 檢視當前是否啟用半同步複製

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| Rpl_semi_sync_slave_status | OFF   |

+----------------------------+-------+

1 row in set (0.00 sec)

 

mysql>

 

-- 主

mysql> show status like 'Rpl_semi_sync_master_clients';  -- 檢視當前半同步從庫數

+------------------------------+-------+

| Variable_name                | Value |

+------------------------------+-------+

| Rpl_semi_sync_master_clients | 0     |

+------------------------------+-------+

1 row in set (0.00 sec)

 

mysql> show status like 'Rpl_semi_sync_master_status';   -- 檢視當前是否啟用半同步複製

+-----------------------------+-------+

| Variable_name               | Value |

+-----------------------------+-------+

| Rpl_semi_sync_master_status | ON    |

+-----------------------------+-------+

1 row in set (0.00 sec)

 

mysql> insert into t1 values(3);

Query OK, 1 row affected (0.00 sec)

 

mysql> commit;

Query OK, 0 rows affected (10.00 sec)

 

mysql> select * from t1;

+------+

| a    |

+------+

|    1 |

|    2 |

|    3 |

+------+

3 rows in set (0.00 sec)

 

mysql> show status like 'Rpl_semi_sync_master_status';

+-----------------------------+-------+

| Variable_name               | Value |

+-----------------------------+-------+

| Rpl_semi_sync_master_status | OFF   |

+-----------------------------+-------+

1 row in set (0.01 sec)

 

mysql>

        可以看到,主庫提交後等待了10秒(rpl_semi_sync_master_timeout的預設值)才完成,而且此時主庫已經降級為非同步複製。


-- 啟動一個從庫的複製

mysql> start slave;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from test.t1;

+------+

| a    |

+------+

|    1 |

|    2 |

|    3 |

+------+

3 rows in set (0.00 sec)

 

mysql> show status like 'rpl_semi_sync%';

+----------------------------+-------+

| Variable_name              | Value |

+----------------------------+-------+

| Rpl_semi_sync_slave_status | ON    |

+----------------------------+-------+

1 row in set (0.01 sec)

 

mysql>

 

-- 主

mysql> show variables like 'rpl_semi_sync_master_wait_for_slave_count';

+-------------------------------------------+-------+

| Variable_name                             | Value |

+-------------------------------------------+-------+

| rpl_semi_sync_master_wait_for_slave_count | 1     |

+-------------------------------------------+-------+

1 row in set (0.00 sec)

 

mysql> show status like 'Rpl_semi_sync_master_clients';

+------------------------------+-------+

| Variable_name                | Value |

+------------------------------+-------+

| Rpl_semi_sync_master_clients | 1     |

+------------------------------+-------+

1 row in set (0.00 sec)

 

mysql> show status like 'Rpl_semi_sync_master_status';

+-----------------------------+-------+

| Variable_name               | Value |

+-----------------------------+-------+

| Rpl_semi_sync_master_status | ON    |

+-----------------------------+-------+

1 row in set (0.00 sec)

 

mysql> insert into t1 values(4);

Query OK, 1 row affected (0.00 sec)

 

mysql> commit;

Query OK, 0 rows affected (0.01 sec)

 

mysql> 

 

-- 從

mysql> select * from test.t1;

+------+

| a    |

+------+

|    1 |

|    2 |

|    3 |

|    4 |

+------+

4 rows in set (0.00 sec)

 

mysql> 

        當Rpl_semi_sync_master_clients大於等於rpl_semi_sync_master_wait_for_slave_count時,主庫立即恢復為半同步複製,併傳送那些未被複制的binlog。


4. rpl_semi_sync_master_wait_no_slave為OFF時從庫數小於rpl_semi_sync_master_wait_for_slave_count

-- 關閉rpl_semi_sync_master_wait_no_slave

mysql> set global rpl_semi_sync_master_wait_no_slave=off;

Query OK, 0 rows affected (0.00 sec)

 

mysql> show variables like 'rpl_semi_sync_master_wait_no_slave';

+------------------------------------+-------+

| Variable_name                      | Value |

+------------------------------------+-------+

| rpl_semi_sync_master_wait_no_slave | OFF   |

+------------------------------------+-------+

1 row in set (0.00 sec)

 

mysql> 

 

-- 關閉所有從庫的複製

mysql> stop slave;

Query OK, 0 rows affected (0.01 sec)

 

mysql> 

 

-- 主

mysql> show status like 'Rpl_semi_sync_master_clients';  -- 檢視當前半同步從庫數

+------------------------------+-------+

| Variable_name                | Value |

+------------------------------+-------+

| Rpl_semi_sync_master_clients | 0     |

+------------------------------+-------+

1 row in set (0.00 sec)

 

mysql> show status like 'Rpl_semi_sync_master_clients';

+------------------------------+-------+

| Variable_name                | Value |

+------------------------------+-------+

| Rpl_semi_sync_master_clients | 0     |

+------------------------------+-------+

1 row in set (0.00 sec)

 

mysql> show status like 'Rpl_semi_sync_master_status';

+-----------------------------+-------+

| Variable_name               | Value |

+-----------------------------+-------+

| Rpl_semi_sync_master_status | OFF   |

+-----------------------------+-------+

1 row in set (0.00 sec)

 

mysql> insert into t1 values(5);

Query OK, 1 row affected (0.00 sec)

 

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

 

mysql> select * from t1;

+------+

| a    |

+------+

|    1 |

|    2 |

|    3 |

|    4 |

|    5 |

+------+

5 rows in set (0.00 sec)

 

mysql> 

        可以看到,當rpl_semi_sync_master_wait_no_slave設定為OFF時,一旦Rpl_semi_sync_master_clients小於rpl_semi_sync_master_wait_for_slave_count,Rpl_semi_sync_master_status立即變為OFF,即立即降為非同步複製。

————————————————

版權宣告:本文為CSDN博主「wzy0623」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結及本宣告。

原文連結:

https://blog.csdn.net/wzy0623/article/details/90267132





About Me

........................................................................................................................

● 本文作者:小麥苗,部分內容整理自網路,若有侵權請聯絡小麥苗刪除

● 本文在itpub、部落格園、CSDN和個人微 信公眾號( xiaomaimiaolhr)上有同步更新

● 本文itpub地址: http://blog.itpub.net/26736162

● 本文部落格園地址: http://www.cnblogs.com/lhrbest

● 本文CSDN地址: https://blog.csdn.net/lihuarongaini

● 本文pdf版、個人簡介及小麥苗雲盤地址: http://blog.itpub.net/26736162/viewspace-1624453/

● 資料庫筆試面試題庫及解答: http://blog.itpub.net/26736162/viewspace-2134706/

● DBA寶典今日頭條號地址: http://www.toutiao.com/c/user/6401772890/#mid=1564638659405826

........................................................................................................................

● QQ群號: 230161599 、618766405

● 微 信群:可加我微 信,我拉大家進群,非誠勿擾

● 聯絡我請加QQ好友 646634621 ,註明新增緣由

● 於 2020-02-01 06:00 ~ 2020-02-31 24:00 在西安完成

● 最新修改時間:2020-02-01 06:00 ~ 2020-02-31 24:00

● 文章內容來源於小麥苗的學習筆記,部分整理自網路,若有侵權或不當之處還請諒解

● 版權所有,歡迎分享本文,轉載請保留出處

........................................................................................................................

小麥苗的微店https://weidian.com/s/793741433?wfr=c&ifr=shopdetail

小麥苗出版的資料庫類叢書http://blog.itpub.net/26736162/viewspace-2142121/

小麥苗OCP、OCM、高可用網路班http://blog.itpub.net/26736162/viewspace-2148098/

小麥苗騰訊課堂主頁https://lhr.ke.qq.com/

........................................................................................................................

使用 微 信客戶端掃描下面的二維碼來關注小麥苗的微 信公眾號( xiaomaimiaolhr)及QQ群(DBA寶典)、新增小麥苗微 信, 學習最實用的資料庫技術。

........................................................................................................................

歡迎與我聯絡

 

 



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2675266/,如需轉載,請註明出處,否則將追究法律責任。

相關文章