半同步複製官方手冊:https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.htmlhtml
默認狀況下,MySQL的複製是異步的,master將新生成的binlog發送給各slave後,無需等待slave的ack回覆(slave將接收到的binlog寫進relay log後纔會回覆ack),直接就認爲此次DDL/DML成功了。mysql
半同步複製(semi-synchronous replication)是指master在將新生成的binlog發送給各slave時,只需等待一個(默認)slave返回的ack信息就返回成功。sql
MySQL 5.7對半同步複製做了大改進,新增了一個master線程。在MySQL 5.7之前,master上的binlog dump線程負責兩件事:dump日誌給slave的io_thread;接收來自slave的ack消息。它們是串行方式工做的。在MySQL 5.7中,新增了一個專門負責接受ack消息的線程ack collector thread。這樣master上有兩個線程獨立工做,能夠同時發送binlog到slave和接收slave的ack。數據庫
還新增了幾個變量,其中最重要的是 rpl_semi_sync_master_wait_point ,它使得MySQL半同步複製有兩種工做模型。解釋以下。安全
從MySQL 5.7.2開始,MySQL支持兩種類型的半同步複製。這兩種類型由變量 rpl_semi_sync_master_wait_point (MySQL 5.7.2以前沒有該變量)控制,它有兩種值:AFTER_SYNC和AFTER_COMMIT。在MySQL 5.7.2以後,默認值爲AFTER_SYNC,在此版本以前,等價的類型爲AFTER_COMMIT。session
這個變量控制的是master什麼時候提交、什麼時候接收ack以及什麼時候回覆成功信息給客戶端的時間點。併發
AFTER_SYNC
模式:master將新的事務寫進binlog(buffer),而後發送給slave,再sync到本身的binlog file(disk)。以後才容許接收slave的ack回覆,接收到ack以後纔會提交事務,並返回成功信息給客戶端。AFTER_COMMIT
模式:master將新的事務寫進binlog(buffer),而後發送給slave,再sync到本身的binlog file(disk),而後直接提交事務。以後才容許接收slave的ack回覆,而後再返回成功信息給客戶端。畫圖理解就很清晰。(前提:已經設置了sync_binlog=1
,不然binlog刷盤時間由操做系統決定)異步
再來分析下這兩種模式的優缺點。socket
AFTER_SYNC
:
AFTER_COMMIT
:
在MySQL 5.7.2以前,等價的模式是 AFTER_COMMIT ,在此版本以後,默認的模式爲 AFTER_SYNC ,該模式能最大程度地保證數據安全性,且性能上並不比 AFTER_COMMIT 差。ide
MySQL的半同步是經過加載google爲MySQL提供的半同步插件 semisync_master.so 和 semisync_slave.so 來實現的。其中前者是master上須要安裝的插件,後者是slave上須要安裝的插件。
MySQL的插件位置默認存放在$basedir/lib/plugin
目錄下。例如,yum安裝的mysql-server,插件目錄爲/usr/lib64/mysql/plugin。
[root@xuexi ~]# find / -type f -name "semisync*" /usr/lib64/mysql/plugin/debug/semisync_master.so /usr/lib64/mysql/plugin/debug/semisync_slave.so /usr/lib64/mysql/plugin/semisync_master.so /usr/lib64/mysql/plugin/semisync_slave.so
由於要加載插件,因此應該保證須要加載插件的MySQL的全局變量 have_dynamic_loading 已經設置爲YES(默認值就是YES),不然沒法動態加載插件。
mysql> select @@global.have_dynamic_loading; +-------------------------------+ | @@global.have_dynamic_loading | +-------------------------------+ | YES | +-------------------------------+
安裝插件有兩種方式:1.在mysql環境中使用INSTALL PLUGIN
語句臨時安裝;2.在配置文件中配置永久生效。
INSTALL安裝插件的語法爲:
Syntax: INSTALL PLUGIN plugin_name SONAME 'shared_library_name' UNINSTALL PLUGIN plugin_name
例如,使用INSTALL語句在master上安裝 semisync_master.so 插件。
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
配置文件中加載插件的方式爲:
[mysqld] plugin-load='plugin_name=shared_library_name'
例如,配置文件中加載semisync_master.so
插件。
[mysqld] plugin-load="rpl_semi_sync_master=sermisync_master.so"
若是須要加載多個插件,則插件之間使用分號分隔。例如,在本節的slave1既是slave,又是master,須要同時安裝兩個半同步插件。
[mysqld] plugin-load="rpl_semi_sync_master=semisync_master.so;rpl_sync_slave=semisync_slave.so"
安裝插件後,應該使用show plugins
來查看插件是否真的激活。
mysql> show plugins; +----------------------+--------+-------------+--------------------+---------+ | Name | Status | Type | Library | License | +----------------------+--------+-------------+--------------------+---------+ ...... | rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL | +----------------------+--------+-------------+--------------------+---------+
或者查看information_schema.plugins
表獲取更詳細的信息。
mysql> select * from information_schema.plugins where plugin_name like "%semi%"\G *************************** 1. row *************************** PLUGIN_NAME: rpl_semi_sync_master PLUGIN_VERSION: 1.0 PLUGIN_STATUS: ACTIVE PLUGIN_TYPE: REPLICATION PLUGIN_TYPE_VERSION: 4.0 PLUGIN_LIBRARY: semisync_master.so PLUGIN_LIBRARY_VERSION: 1.7 PLUGIN_AUTHOR: He Zhenxing PLUGIN_DESCRIPTION: Semi-synchronous replication master PLUGIN_LICENSE: GPL LOAD_OPTION: ON 1 row in set (0.00 sec)
插件裝載完成後,半同步功能還未開啓,須要手動設置它們啓動,或者寫入配置文件永久生效。
# 開啓master的半同步 mysql> set @@global.rpl_semi_sync_master_enabled=1; # 開啓slave半同步 mysql> set @@globale.rpl_semi_sync_slave_enabled=1;
或者配合插件加載選項一塊兒寫進配置文件永久開啓半同步功能。
[mysqld] rpl_semi_sync_master_enabled=1 [mysqld] rpl_semi_sync_slave_enabled=1
安裝了 semisync_master.so 和 semisync_slave.so 後,這兩個插件分別提供了幾個變量。
mysql> show global 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 | | rpl_semi_sync_slave_enabled | OFF | | rpl_semi_sync_slave_trace_level | 32 | +-------------------------------------------+------------+
下面還多給了兩個和半同步相關的狀態變量的解釋,能夠經過show status like %semi%;
查看它們。
Rpl_semi_sync_master_clients
:(狀態變量)master所擁有的半同步複製slave的主機數量。Rpl_semi_sync_master_status
:(狀態變量)master當前是否以半同步複製狀態工做(ON),OFF表示降級爲了異步複製。rpl_semi_sync_master_enabled
:master上是否啓用了半同步複製。rpl_semi_sync_master_timeout
:等待slave的ack回覆的超時時間,默認爲10秒。rpl_semi_sync_master_trace_level
:半同步複製時master的調試級別。rpl_semi_sync_master_wait_for_slave_count
:master在超時時間內須要收到多少個ack回覆才認爲這次DML成功,不然就降級爲異步複製。該變量在MySQL5.7.3才提供,在此以前的版本都默認爲收到1個ack則確認成功,且不可更改。MySQL 5.7.3以後該變量的默認值也是1。⑦.rpl_semi_sync_master_wait_no_slave
:值爲ON(默認)或者OFF。ON表示master在超時時間內若是未收到指定數量的ack消息,則會一直等待下去直到收滿ack,即一直採用半同步複製方式,不會降級;OFF表示若是在超時時間內未收到指定數量的ack,則超時時間一過當即降級爲異步複製。
更官方的解釋是:當設置爲ON時,即便狀態變量Rpl_semi_sync_master_clients中的值小於rpl_semi_sync_master_wait_for_slave_count,Rpl_semi_sync_master_status依舊爲ON;當設置爲OFF時,若是clients的值小於count的值,則Rpl_semi_sync_master_status當即變爲OFF。通俗地講,就是在超時時間內,若是slave宕機的數量超過了應該要收到的ack數量,master是否降級爲異步複製。
該變量在MySQL 5.7.3以前彷佛沒有效果,由於默認設置爲ON時,超時時間內收不到任何ack時仍然會降級爲異步複製。
⑧.rpl_semi_sync_master_wait_point
:控制master上commit、接收ack、返回消息給客戶端的時間點。值爲 AFTER_SYNC 和 AFTER_COMMIT ,該選項是MySQL5.7.2後引入的,默認值爲 AFTER_SYNC ,在此版本以前,等價於使用了 AFTER_COMMIT 模式。關於這兩種模式,見前文對兩種半同步類型的分析。
rpl_semi_sync_slave_enabled
:slave是否開啓半同步複製。rpl_semi_sync_slave_trace_level
:slave的調試級別。須要注意的是,"半同步"是同步/異步類型的一種狀況,既能夠實現半同步的傳統複製,也能夠實現半同步的GTID複製。其實半同步複製是基於異步複製的,它是在異步複製的基礎上經過加載半同步插件的形式來實現半同步性的。
此處以全新的環境進行配置,方便各位道友"依葫蘆畫瓢"。
本文實現以下拓撲圖所示的半同步傳統複製。若是要實現半同步GTID複製,也只是在gtid複製的基礎上改改配置文件而已。
具體環境:
稱呼 | 主機IP | MySQL版本 | OS | 角色(master/slave) | 數據庫狀態 |
---|---|---|---|---|---|
master | 192.168.100.21 | MySQL 5.7.22 | CentOS 7.2 | master | 全新實例 |
salve1 | 192.168.100.22 | MySQL 5.7.22 | CentOS 7.2 | semi_slave for master semi_master for other slaves |
全新實例 |
slave2 | 192.168.100.23 | MySQL 5.7.22 | CentOS 7.2 | semi_slave for slave1 | 全新實例 |
slave3 | 192.168.100.24 | MySQL 5.7.22 | CentOS 7.2 | semi_slave for slave1 | 全新實例 |
由於都是全新的實例環境,因此無需考慮基準數據和binlog座標的問題。若是開始測試前,已經在master上作了一些操做,或者建立了一些新數據,那麼請將master上的數據恢復到各slave上,並獲取master binlog的座標,具體操做方法可參見前文:將slave恢復到master指定的座標。
首先提供各MySQL Server的配置文件。
如下是master的配置文件。
[mysqld] datadir=/data socket=/data/mysql.sock log-error=/data/error.log pid-file=/data/mysqld.pid log-bin=/data/master-bin sync-binlog=1 server-id=100 plugin-load="rpl_semi_sync_master=semisync_master.so" rpl_semi_sync_master_enabled=1
如下是slave1的配置文件,注意slave1同時還充當着slave2和slave3的master的角色。
[mysqld] datadir=/data socket=/data/mysql.sock log-error=/data/error.log pid-file=/data/mysqld.pid log-bin=/data/master-bin sync-binlog=1 server-id=110 relay-log=/data/slave-bin log-slave-updates plugin-load="rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so" rpl_semi_sync_slave_enabled=1 rpl_semi_sync_master_enabled=1
如下是slave2和slave3的配置文件,它們配置文件除了server-id外都一致。
[mysqld] datadir=/data socket=/data/mysql.sock log-error=/data/error.log pid-file=/data/mysqld.pid server-id=120 # slave3的server-id=130 relay-log=/data/slave-bin plugin-load="rpl_semi_sync_slave=semisync_slave.so" rpl_semi_sync_slave_enabled=1 read-only=on
如今master上建立一個專門用於複製的用戶。
mysql> create user repl@'192.168.100.%' identified by 'P@ssword1!'; mysql> grant replication slave on *.* to repl@'192.168.100.%';
由於master和全部的slave都是全新的實例,因此slave上指定的binlog座標能夠從任意位置開始。不過剛纔master上建立了一個用戶,也會寫binlog,因此建議仍是從master的第一個binlog的position=4開始。
如下是slave1上的change master to
參數:
mysql> change master to master_host='192.168.100.21', master_port=3306, master_user='repl', master_password='P@ssword1!', master_log_file='master-bin.000001', master_log_pos=4;
如下是slave2和slave3的change master to
參數:
mysql> change master to master_host='192.168.100.22', master_port=3306, master_user='repl', master_password='P@ssword1!', master_log_file='master-bin.000001', master_log_pos=4;
啓動各slave上的兩個SQL線程。
mysql> start slave;
一切就緒後,剩下的事情就是測試。在master上對數據作一番修改,而後查看是否會同步到slave一、slave二、slave3上。
首先是semisync相關的可修改變量,這幾個變量在前文已經解釋過了。
例如如下是開啓了半同步複製後的master上的semisync相關變量。
mysql> show global variables like "%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 | +-------------------------------------------+------------+
關於半同步複製,還有幾個狀態變量很重要。
例如,如下是master上關於semi_sync的狀態變量信息。
mysql> show status like "%semi%"; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | # 注意行1 | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 5 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 | | Rpl_semi_sync_master_status | ON | # 注意行2 | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 384 | | Rpl_semi_sync_master_tx_wait_time | 1537 | | Rpl_semi_sync_master_tx_waits | 4 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 4 | +--------------------------------------------+-------+
除了上面標註"注意行"的變量,其餘都無需關注,並且其中有一些是廢棄了的狀態變量。
Rpl_semi_sync_master_clients
是該master所鏈接到的slave數量。
Rpl_semi_sync_master_status
是該master的半同步複製功能是否開啓。在有些時候半同步複製會降級爲異步複製,這時它的值爲OFF。
如下是slave1上關於semi_sync的狀態變量信息。
mysql> show status like "%semi%"; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 2 | # 注意行1 | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 8 | | Rpl_semi_sync_master_no_times | 2 | | Rpl_semi_sync_master_no_tx | 4 | | Rpl_semi_sync_master_status | ON | # 注意行2 | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 399 | | Rpl_semi_sync_master_tx_wait_time | 1199 | | 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 | | Rpl_semi_sync_slave_status | ON | # 注意行3 +--------------------------------------------+-------+
此外,從MySQL的錯誤日誌、show slave status
也能獲取到一些半同步複製的狀態信息。下一節測試半同步複製再說明。
前面已經搭建好了下面的半同步複製結構。
|------> slave2 master ---> slave1 --- |------> slave3
下面來測試半同步複製降級爲異步複製的問題,藉此來觀察一些semisync的狀態變化。
首先,只停掉slave2或slave3中其中一個io線程的話,slave1是不會出現降級的,由於默認的半同步複製只需等待一個ack回覆便可返回成功信息。
若是同時停掉slave2和slave3的io線程,當master更新數據後,slave1在10秒(默認)以後將降級爲異步複製。以下:
在slave2和slave3上執行:
mysql> stop slave io_thread;
在master上執行:
create database test1; create table test1.t(id int); insert into test1.t values(33);
在slave1上查看(在上面的步驟以後的10秒內查看):
mysql> show status like "%semi%"; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 0 | # clients=0 | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 8 | | Rpl_semi_sync_master_no_times | 2 | | Rpl_semi_sync_master_no_tx | 4 | | Rpl_semi_sync_master_status | ON | # status=ON | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 399 | | Rpl_semi_sync_master_tx_wait_time | 1199 | | Rpl_semi_sync_master_tx_waits | 3 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 1 | | Rpl_semi_sync_master_yes_tx | 3 | | Rpl_semi_sync_slave_status | ON | +--------------------------------------------+-------+
能夠看到在這一小段時間內,slave1仍是半同步複製。此時用show slave status
查看slave1。
# slave1上執行 mysql> show slave status \G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.100.21 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000004 Read_Master_Log_Pos: 1762 Relay_Log_File: slave-bin.000005 Relay_Log_Pos: 1729 Relay_Master_Log_File: master-bin.000004 Slave_IO_Running: Yes Slave_SQL_Running: Yes ................................................ Slave_SQL_Running_State: Waiting for semi-sync ACK from slave ................................................
此時slave的SQL線程狀態是Waiting for semi-sync ACK from slave
。
但10秒以後再查看。
mysql> show status like "%semi%"; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 0 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 8 | | Rpl_semi_sync_master_no_times | 3 | | Rpl_semi_sync_master_no_tx | 5 | | Rpl_semi_sync_master_status | OFF | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 399 | | Rpl_semi_sync_master_tx_wait_time | 1199 | | 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 | | Rpl_semi_sync_slave_status | ON | +--------------------------------------------+-------+
發現slave1已經關閉半同步功能了,也就是說降級爲異步複製了。
此時查看slave1的錯誤日誌。
2018-06-11T03:43:21.765384Z 4 [Warning] Timeout waiting for reply of binlog (file: master-bin.000001, pos: 2535), semi-sync up to file master-bin.000001, position 2292. 2018-06-11T03:43:21.765453Z 4 [Note] Semi-sync replication switched OFF.
它先記錄了當前slave2/slave3中已經同步到slave1的哪一個位置。而後將Semi-sync複製切換爲OFF狀態,即降級爲異步複製。
在下次slave2或slave3啓動IO線程時,slave1將自動切換回半同步複製,併發送那些未被複制的binlog。