原理簡介:前端
在MySQL5.5以前,MySQL的複製其實都是異步複製(見下圖),主庫和從庫的數據之間存在必定的延遲,這樣存在一個隱患:當在主庫上寫入一個事務並提交成功,而從庫還沒有獲得主庫推送的BinLog日誌時,剛好主庫宕機了,例如主庫可能因磁盤損壞、內存故障等形成主庫上該事務Binlog丟失,此時從庫就可能損失這個事務,從而形成主從不一致。mysql
爲了解決這個問題,從MySQL5.5開始引入了半同步複製機制(Semi_synchronous Replication)。爲了保證主庫上的每個Binlog事務都可以被可靠的複製到從庫上,主庫在每次事務成功提交時,並不及時反饋給前端用戶,而是等待其中一個從庫也接受到Binlog事務併成功寫入中繼日誌後,主庫才返回Commit操做成功給客戶端。半同步複製保證了事務成功提交後,至少有兩份日誌記錄,一份在主庫的Binlog日誌上,另外一份在至少一個從庫的中繼日誌Relay Log上,從而更進一步保證了數據的完整性。半同步複製的大體流程以下圖:sql
半同步複製模式下,假如在上圖步驟①②③中任何一個步驟中主庫宕機,則事務並未提交成功,從庫上也沒收到事務對應的Binlog日誌,因此主從數據是一致的;假如在步驟④傳送Binlog日誌到從庫時,從庫宕機或者網絡故障,致使Binlog並無及時地傳送到從庫上,此時主庫上的事務會等待一段時間(時間長短由參數rpl_semi_sync_master_timeout設置的毫秒數決定),若是Binlog在這段時間內都沒法成功推送到從庫上,則MySQL自動調整複製爲異步複製,事務正常返回提交結果給客戶端。服務器
半同步複製很大程度上取決於主從庫之間的網絡狀況,往返時延RTT(Round-Trip Time)越小決定了從庫的實時性越好。通俗地說,主從庫之間的網絡越快,從庫越實時。網絡
測試環境:session
安裝步驟:異步
半同步複製是以插件形式來實現的,安裝比較簡單,在異步複製的環境上(這裏已經安裝好傳統的異步複製),安裝半同步插件便可,也可在新建時寫入my.cnf,參考文章尾部;測試
1. 查看MySQL服務器是否支持動態增長插件spa
mysql> select @@have_dynamic_loading; +------------------------+ | @@have_dynamic_loading | +------------------------+ | YES | //YES表示支持 +------------------------+ 1 row in set (0.00 sec)
2. 確認支持動態插件後,檢查安裝目錄是否存在所需插件,通常在mysql安裝目錄中的一個.../plugin/目錄下,能夠搜索一下:插件
[root@server-10 ~]# find / -name semisync_*.so /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
3. 在主庫上安裝插件semisync_master.so
mysql> install plugin rpl_semi_sync_master SONAME 'semisync_master.so'; Query OK, 0 rows affected (0.03 sec)
4. 在從庫上安裝插件semisync_master.so
mysql> install plugin rpl_semi_sync_slave SONAME 'semisync_slave.so'; Query OK, 0 rows affected (0.00 sec)
插件安裝完成後,從plugin表中可以看到剛纔安裝的插件(這裏在主庫上查看一下):
mysql> select * from mysql.plugin; +----------------------+--------------------+ | name | dl | +----------------------+--------------------+ | rpl_semi_sync_master | semisync_master.so | +----------------------+--------------------+ 1 row in set (0.00 sec)
也就是說,安裝完成後,MySQL會在系統表plugin中記錄剛纔安裝的插件,下次系統重啓後會自動加載插件。
5. 分別在主庫和從庫上配置參數打開半同步semi-sync,默認半同步設置是不打開的。
在主庫上配置全局參數:
mysql> show variables like 'rpl_semi_sync_master%'; +------------------------------------+-------+ | 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_no_slave | ON | +------------------------------------+-------+ 4 rows in set (0.00 sec) mysql> set global rpl_semi_sync_master_enabled = 1; Query OK, 0 rows affected (0.00 sec) mysql> set global rpl_semi_sync_master_timeout = 20000; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'rpl_semi_sync_master%'; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 20000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_no_slave | ON | +------------------------------------+-------+ 4 rows in set (0.00 sec)
在從庫上配置參數:
mysql> set global rpl_semi_sync_slave_enabled = 1; Query OK, 0 rows affected (0.00 sec)
6. 因爲以前配置的是傳統的異步複製,因此須要重啓一下從庫上的I/O線程(若是是全新配置的半同步複製則不須要,後面會提到全新配置):
mysql> stop slave io_thread; Query OK, 0 rows affected (0.05 sec) mysql> start slave io_thread; Query OK, 0 rows affected (0.00 sec)
到此,半同步複製配置完成,下面能夠來驗證一下。
實際測試:
1. 先查看當前主庫上半同步複製的一些狀態值:
mysql> show status like 'Rpl_semi_sync%'; +--------------------------------------------+-------+ | 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)
注意:環境不同,可能顯示也不同,着重關注如下3個狀態值的變化,而不是上面這些初始值。
Rpl_semi_sync_master_status :值爲ON,表示半同步複製目前處於打開狀態。
Rpl_semi_sync_master_yes_tx:值爲0,表示主庫當前還沒有有任何一個事務是經過半同步複製到從庫。
Rpl_semi_sync_master_no_tx:值爲0,表示當前有0個事務不是半同步模式下從庫及時響應的。
在主庫上執行一個事務,而後再檢查一下狀態:
mysql> use mydb; Database changed mysql> show tables; Empty set (0.00 sec) mysql> CREATE TABLE customers -> ( -> cust_id int NOT NULL AUTO_INCREMENT, -> cust_name char(50) NOT NULL , -> cust_address char(50) NULL , -> cust_city char(50) NULL , -> cust_state char(5) NULL , -> cust_zip char(10) NULL , -> cust_country char(50) NULL , -> cust_contact char(50) NULL , -> cust_email char(255) NULL , -> PRIMARY KEY (cust_id) -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.10 sec) mysql> show tables; +----------------+ | Tables_in_mydb | +----------------+ | customers | +----------------+ 1 row in set (0.00 sec) mysql> show status like 'Rpl_semi_sync%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 316 | | Rpl_semi_sync_master_net_wait_time | 316 | | Rpl_semi_sync_master_net_waits | 1 | | 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 | 488 | | Rpl_semi_sync_master_tx_wait_time | 488 | | Rpl_semi_sync_master_tx_waits | 1 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 1 | +--------------------------------------------+-------+ 14 rows in set (0.00 sec)
此時會發現Rpl_semi_sync_master_yes_tx的值變爲1,即剛纔的CREATE事務經過半同步複製到從庫上了,Rpl_semi_sync_master_yes_tx計數增長1。
到從庫確認一下,新建的customers表確實被複制過去了:
2. 接下來模仿網絡異常的場景下,主庫在等待 rpl_semi_sync_master_timeout毫秒超時後,自動轉成異步複製的場景。
在主庫上確認半同步複製會等待20s超時:
mysql> show variables like 'rpl_semi_sync_master_timeout'; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | rpl_semi_sync_master_timeout | 20000 | //單位:ms +------------------------------+-------+ 1 row in set (0.00 sec)
在從庫上經過iptables命令模擬從庫宕機或者網絡故障:
[root@server-11 ~]# iptables -A INPUT -s 138.138.82.10 -j DROP
在主庫上執行一個事務並提交(默認提交便可),主庫上的提交操做會被阻塞20秒:
mysql> INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) -> VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com'); Query OK, 1 row affected (20.05 sec) //回車後,會卡主(阻塞)20秒,而後纔會跳出Query OK...這行,並顯示用時20秒
在這個20秒阻塞過程當中,新開一個窗口檢查當前主庫的線程,會發現提交操做在等待從庫上半同步複製操做的響應:
mysql> show processlist\G
......
*************************** 3. row *************************** Id: 12 User: root Host: localhost db: mydb Command: Query Time: 3 State: Waiting for semi-sync ACK from slave //阻塞,等待從庫確認 Info: INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_countr 3 rows in set (0.00 sec)
阻塞結束後,再次查看半同步複製的一些狀態值:
mysql> show status like 'Rpl_semi_sync%'; +--------------------------------------------+----------+ | Variable_name | Value | +--------------------------------------------+----------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 15011119 | | Rpl_semi_sync_master_net_wait_time | 30022238 | | Rpl_semi_sync_master_net_waits | 2 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 |//該值更新爲1,表示在半同步複製模式下,從庫沒有及時響應的事務增長1個 | Rpl_semi_sync_master_status | OFF |//表示主庫上半同步複製已經關閉了 | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 488 | | Rpl_semi_sync_master_tx_wait_time | 488 | | Rpl_semi_sync_master_tx_waits | 1 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 1 |//該值仍然爲1,表示剛纔的事務並非經過半同步複製完成的,因此半同步成功事務仍然爲1個 +--------------------------------------------+----------+ 14 rows in set (0.00 sec)
繼續測試:若是從庫正常鏈接上主庫以後,主庫是否會自動切換回半同步複製模式呢?
那麼把以前從庫上面的iptables限制條目去除:
[root@server-11 ~]# iptables -F [root@server-11 ~]# iptables -nL //查看一下確實沒了 Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
而後在從庫上查看slave狀態:
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 138.138.82.10 Master_User: repl_user Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000010 Read_Master_Log_Pos: 988 Relay_Log_File: relay-bin.000016 Relay_Log_Pos: 704 Relay_Master_Log_File: master-bin.000010 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 988 Relay_Log_Space: 5483 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 10 Master_UUID: 8086bac0-a428-11e8-8bf9-00505691656b Master_Info_File: /var/lib/mysql/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0 1 row in set (0.00 sec) mysql> select * from customers; //以前在主庫插入時阻塞20.05秒的條目也複製過來了 +---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+ | cust_id | cust_name | cust_address | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email | +---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+ | 10001 | Coyote Inc. | 200 Maple Lane | Detroit | MI | 44444 | USA | Y Lee | ylee@coyote.com | +---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+ 1 row in set (0.00 sec)
以上顯示說明在網絡狀態恢復後(去掉iptables),從庫會自動嘗試鏈接主庫,幾秒鐘後I/O線程狀態從Connecting變成了YES,而且主庫和從庫的數據一致了。
再次查看主庫上半同步複製的狀態值:
mysql> show status like 'Rpl_semi_sync%'; +--------------------------------------------+----------+ | Variable_name | Value | +--------------------------------------------+----------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 10033583 | | Rpl_semi_sync_master_net_wait_time | 30100750 | | Rpl_semi_sync_master_net_waits | 3 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 | | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 488 | | Rpl_semi_sync_master_tx_wait_time | 488 | | Rpl_semi_sync_master_tx_waits | 1 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 1 | +--------------------------------------------+----------+ 14 rows in set (0.00 sec)
以上發現Rpl_semi_sync_master_status的值自動從OFF變成ON,說明在檢測到從庫正常以後,主庫到從庫的複製方式會自動切換爲半同步複製模式。
咱們繼續在主庫上作一個INSERT事務測試,確認當前的複製模式確實是半同步複製:
mysql> INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact) -> VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse'); Query OK, 1 row affected (0.07 sec) mysql> show status like 'Rpl_semi_sync%'; +--------------------------------------------+----------+ | Variable_name | Value | +--------------------------------------------+----------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 7525296 | | Rpl_semi_sync_master_net_wait_time | 30101185 | | Rpl_semi_sync_master_net_waits | 4 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 1 | | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 517 | | Rpl_semi_sync_master_tx_wait_time | 1034 | | Rpl_semi_sync_master_tx_waits | 2 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 2 | //計數增長了1個,變爲2 +--------------------------------------------+----------+ 14 rows in set (0.00 sec)
能夠看出,以上的一個INSERT事務提交後,Rpl_semi_sync_master_yes_tx 值從1變成2,確認了剛纔事務的複製事半同步複製。
測試結束;
小結:
從半同步複製的流程會發現,半同步複製的「半」就體如今:雖然主庫和從庫的Binlog日誌時同步的,可是主庫並不等待從庫應用這部分日誌就返回提交結果,這部分操做是異步的,從庫的數據並非和主庫實時同步的,因此只能成爲半同步,而不是徹底的實時同步。
補充:
經過配置文件添加半同步插件和參數,操做以下:
在/etc/my.cnf中添加如下參數
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
rpl_semi_sync_master_timeout = 20000
結束.