MySQL主從複製包括異步模式、半同步模式、GTID模式以及多源複製模式,默認是異步模式 (如以前詳細介紹的mysql主從複製)。所謂異步模式指的是MySQL 主服務器上I/O thread 線程將二進制日誌寫入binlog文件以後就返回客戶端結果,不會考慮二進制日誌是否完整傳輸到從服務器以及是否完整存放到從服務器上的relay日誌中,這種模式一旦主服務(器)宕機,數據就可能會發生丟失。html
異步模式是一種基於偏移量的主從複製,實現原理是: 主庫開啓binlog功能並受權從庫鏈接主庫,從庫經過change master獲得主庫的相關同步信息而後鏈接主庫進行驗證,主庫IO線程根據從庫slave線程的請求,從master.info開始記錄的位置點向下開始取信息,同時把取到的位置點和最新的位置與binlog信息一同發給從庫IO線程,從庫將相關的sql語句存放在relay-log裏面,最終從庫的sql線程將relay-log裏的sql語句應用到從庫上,至此整個同步過程完成,以後將是無限重複上述過程。mysql
mysql主從複製的步驟: 1)在主庫與從庫都安裝mysql數據庫; 2) 在主庫的my.cnf配置文件中配置server-id 和log-bin; 3) 在登錄主庫後建立認證用戶並作受權; 4) 在從庫的my.cnf配置文件中配置server-id; 5) 登錄從庫後,指定master並開啓同步開關。linux
須要注意的是server-id主從庫的配置是不同的。sql
server-id存在做用: mysql同步的數據中是包含server-id的,而server-id用於標識該語句最初是從哪一個server寫入的。所以server-id必定要有的shell
server-id不能相同的緣由:每個同步中的slave在master上都對應一個master線程,該線程就是經過slave的server-id來標識的;每一個slave在master端最多有一個master線程,若是兩個slave的server-id 相同,則後一個鏈接成功時,slave主動鏈接master以後,若是slave上面執行了slave stop;則鏈接斷開,可是master上對應的線程並無退出;當slave start以後,master不能再建立一個線程而保留原來的線程,那樣同步就可能有問題;數據庫
在mysql作主主同步時,多個主須要構成一個環狀,可是同步的時候又要保證一條數據不會陷入死循環,這裏就是靠server-id來實現的;vim
描述msyql replication 機制的實現原理,如何在不停掉mysql主庫的狀況下,恢復數據不一致的slave的數據庫節點?數組
MySQL的複製(replication)是一個異步的複製,從一個MySQL instace(稱之爲Master)複製到另外一個MySQL instance(稱之Slave)。實現整個複製操做主要由三個進程完成的,其中兩個進程在Slave(Sql進程和IO進程),另一個進程在Master(IO進程)上。簡單來講,Mysql複製就是一種基於binlog的單線程異步複製過程!!安全
MySQL Replication複製的基本過程以下:
1) Slave上面的IO進程鏈接上Master,並請求從指定日誌文件的指定位置(或者從最開始的日誌)以後的日誌內容bash
mysql> CHANGE MASTER TO -> MASTER_HOST='master_host_name', -> MASTER_USER='replication_user_name', -> MASTER_PASSWORD='replication_password', -> MASTER_LOG_FILE='recorded_log_file_name', -> MASTER_LOG_POS=recorded_log_position; -> MASTER_CONNECT_RETRY=10; #鏈接主庫失敗時,每隔10s鍾就從新連一下! 通常省略這一行配置,不用配置這一行。
2) Master接收到來自Slave的IO進程的請求後,經過負責複製的IO進程根據請求信息讀取制定日誌指定位置以後的日誌信息,返回給Slave的IO進程。返回信息中除了日誌所包含的信息以外,還包括本次返回的信息已經到Master端的bin-log文件的名稱以及bin-log的位置;
3) Slave的IO進程接收到信息後,將接收到的日誌內容依次添加到Slave端的relay-log文件的最末端,並將讀取到的Master端的bin-log的文件名和位置記錄到master-info文件中,以便在下一次讀取的時候可以清楚的高速Maste "我須要從某個bin-log的哪一個位置開始日後的日誌內容,請發給我"!
4) Slave的Sql進程檢測到relay-log中新增長了內容後,會立刻解析relay-log的內容成爲在Master端真實執行時候的那些可執行的內容,並在自身執行。
上面提到的是mysql默認的異步同步模式,接下來重點說下Mysql半同步複製,從MySQL5.5開始,MySQL以插件的形式支持半同步複製。先來區別下mysql幾個同步模式概念:
異步複製(Asynchronous replication)
MySQL默認的複製便是異步的,主庫在執行完客戶端提交的事務後會當即將結果返給給客戶端,並不關心從庫是否已經接收並處理,這樣就會有一個問題,主若是crash掉了,此時主上已經提交的事務可能並無傳到從上,若是此時,強行將從提高爲主,可能致使新主上的數據不完整。
全同步複製(Fully synchronous replication)
指當主庫執行完一個事務,全部的從庫都執行了該事務才返回給客戶端。由於須要等待全部從庫執行完該事務才能返回,因此全同步複製的性能必然會收到嚴重的影響。
半同步複製(Semisynchronous replication)
介於異步複製和全同步複製之間,主庫在執行完客戶端提交的事務後不是馬上返回給客戶端,而是等待至少一個從庫接收到並寫到relay log中才返回給客戶端。相對於異步複製,半同步複製提升了數據的安全性,同時它也形成了必定程度的延遲,這個延遲最少是一個TCP/IP往返的時間。因此,半同步複製最好在低延時的網絡中使用。
- 對於異步複製,主庫將事務Binlog事件寫入到Binlog文件中,此時主庫只會通知一下Dump線程發送這些新的Binlog,而後主庫就會繼續處理提交操做,而此時不會保證這些Binlog傳到任何一個從庫節點上。
- 對於全同步複製,當主庫提交事務以後,全部的從庫節點必須收到,APPLY而且提交這些事務,而後主庫線程才能繼續作後續操做。這裏面有一個很明顯的缺點就是,主庫完成一個事務的時間被拉長,性能下降。
- 對於半同步複製,是介於全同步複製和異步複製之間的一種,主庫只須要等待至少一個從庫節點收到而且Flush Binlog到Relay Log文件便可,主庫不須要等待全部從庫給主庫反饋。同時,這裏只是一個收到的反饋,而不是已經徹底執行而且提交的反饋,這樣就節省了不少時間。
Mysql半同步複製技術
通常而言,普通的replication,即MySQL的異步複製,依靠MySQL二進制日誌也即binary log進行數據複製。好比兩臺機器,一臺主機(master),另一臺是從機(slave)。
正常的複製爲:事務一(t1)寫入binlog buffer;dumper線程通知slave有新的事務t1;binlog buffer進行checkpoint;slave的io線程接收到t1並寫入到本身的的relay log;slave的sql線程寫入到本地數據庫。 這時,master和slave都能看到這條新的事務,即便master掛了,slave能夠提高爲新的master。
異常的複製爲:事務一(t1)寫入binlog buffer;dumper線程通知slave有新的事務t1;binlog buffer進行checkpoint;slave由於網絡不穩定,一直沒有收到t1;master掛掉,slave提高爲新的master,t1丟失。
很大的問題是:主機和從機事務更新的不一樣步,就算是沒有網絡或者其餘系統的異常,當業務併發上來時,slave由於要順序執行master批量事務,致使很大的延遲。
爲了彌補以上幾種場景的不足,MySQL從5.5開始推出了半同步複製。相比異步複製,半同步複製提升了數據完整性,由於很明確知道,在一個事務提交成功以後,這個事務就至少會存在於兩個地方。即在master的dumper線程通知slave後,增長了一個ack(消息確認),便是否成功收到t1的標誌碼,也就是dumper線程除了發送t1到slave,還承擔了接收slave的ack工做。若是出現異常,沒有收到ack,那麼將自動降級爲普通的複製,直到異常修復後又會自動變爲半同步複製。
半同步複製具體特性
- 從庫會在鏈接到主庫時告訴主庫,它是否是配置了半同步。
- 若是半同步複製在主庫端是開啓了的,而且至少有一個半同步複製的從庫節點,那麼此時主庫的事務線程在提交時會被阻塞並等待,結果有兩種可能,要麼至少一個從庫節點通知它已經收到了全部這個事務的Binlog事件,要麼一直等待直到超過配置的某一個時間點爲止,而此時,半同步複製將自動關閉,轉換爲異步複製。
- 從庫節點只有在接收到某一個事務的全部Binlog,將其寫入並Flush到Relay Log文件以後,纔會通知對應主庫上面的等待線程。
- 若是在等待過程當中,等待時間已經超過了配置的超時時間,沒有任何一個從節點通知當前事務,那麼此時主庫會自動轉換爲異步複製,當至少一個半同步從節點遇上來時,主庫便會自動轉換爲半同步方式的複製。
- 半同步複製必須是在主庫和從庫兩端都開啓時才行,若是在主庫上沒打開,或者在主庫上開啓了而在從庫上沒有開啓,主庫都會使用異步方式複製。
下面來看看半同步複製的原理圖:
半同步複製的意思表示MASTER 只須要接收到其中一臺SLAVE的返回信息,就會commit;不然需等待直至達到超時時間而後切換成異步再提交。這個作可使主從庫的數據的延遲較小,能夠在損失很小的性能的前提下提升數據的安全性。
主庫產生binlog到主庫的binlog file,傳到從庫中繼日誌,而後從庫應用;也就是說傳輸是異步的,應用也是異步的。半同步複製指的是傳輸同步,應用仍是異步的!
好處:保證數據不丟失(本機和遠端都有binlog)
壞處:不能保證應用的同步。
mysql半同步複製模式的流程圖
即主庫突然崩了時,從庫雖說有延遲,可是延遲事後,能夠把從庫提高爲主庫繼續服務,過後恢復到主庫便可
mysql異步複製模式的流程圖
半同步複製的潛在問題
客戶端事務在存儲引擎層提交後,在獲得從庫確認的過程當中,主庫宕機了,此時,可能的狀況有兩種
- 事務還沒發送到從庫上
此時,客戶端會收到事務提交失敗的信息,客戶端會從新提交該事務到新的主上,當宕機的主庫從新啓動後,以從庫的身份從新加入到該主從結構中,會發現,該事務在從庫中被提交了兩次,一次是以前做爲主的時候,一次是被新主同步過來的。
- 事務已經發送到從庫上
此時,從庫已經收到並應用了該事務,可是客戶端仍然會收到事務提交失敗的信息,從新提交該事務到新的主上。
無數據丟失的半同步複製
針對上述潛在問題,MySQL 5.7引入了一種新的半同步方案:Loss-Less半同步複製。針對上面這個圖,"Waiting Slave dump"被調整到"Storage Commit"以前。固然,以前的半同步方案一樣支持,MySQL 5.7.2引入了一個新的參數進行控制: rpl_semi_sync_master_wait_point, 這個參數有兩種取值:1) AFTER_SYNC , 這個是新的半同步方案,Waiting Slave dump在Storage Commit以前。2) AFTER_COMMIT, 這個是老的半同步方案。
來看下面半同步複製原理圖,分析下半同步複製潛在問題
master將每一個事務寫入binlog(sync_binlog=1),傳遞到slave刷新到磁盤(sync_relay=1),同時主庫提交事務(commit)。master等待slave反饋收到relay log,只有收到ACK後master纔將commit OK結果反饋給客戶端。
在MySQL 5.5-5.6使用after_commit的模式下,客戶端事務在存儲引擎層提交後,在獲得從庫確認的過程當中,主庫宕機了。此時,即主庫在等待Slave ACK的時候,雖然沒有返回當前客戶端,但事務已經提交,其餘客戶端會讀取到已提交事務。若是Slave端尚未讀到該事務的events,同時主庫發生了crash,而後切換到備庫。那麼以前讀到的事務就不見了,出現了幻讀。
若是主庫永遠啓動不了,那麼實際上在主庫已經成功提交的事務,在從庫上是找不到的,也就是數據丟失了,這是MySQL不肯意看到的。因此在MySQL 5.7版本中增長了after_sync(無損複製)參數,並將其設置爲默認半同步方式,解決了數據丟失的問題。
半同步複製的安裝部署條件
要想使用半同步複製,必須知足如下幾個條件:
1)MySQL 5.5及以上版本
2)變量have_dynamic_loading爲YES (查看命令:show variables like "have_dynamic_loading";)
3)主從複製已經存在 (即提早部署mysql主從複製環境,主從同步要配置基於整個數據庫的,不要配置基於某個庫的同步,即同步時不要過濾庫)
- 首先加載插件
因用戶需執行INSTALL PLUGIN, SET GLOBAL, STOP SLAVE和START SLAVE操做,因此用戶需有SUPER權限。
半同步複製是一個功能模塊,庫要能支持動態加載才能實現半同步複製! (安裝的模塊存放路徑爲/usr/local/mysql/lib/plugin)
主數據庫執行:
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; [要保證/usr/local/mysql/lib/plugin/目錄下有semisync_master.so文件 (默認編譯安裝後就有)] --------------------------------------------------------------------------------------- 若是要卸載(前提是要關閉半同步複製功能),就執行 mysql> UNINSTALL PLUGIN rpl_semi_sync_master;
從數據庫執行:
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; [要保證/usr/local/mysql/lib/plugin/目錄下有semisync_slave.so文件 (默認編譯安裝後就有)] --------------------------------------------------------------------------------------- 若是要卸載(前提是要關閉半同步複製功能),就執行 mysql> UNINSTALL PLUGIN rpl_semi_sync_slave;
- 查看插件是否加載成功的兩種方式:
1) 方式一
mysql> show plugins; ........ | rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL |
2) 方式二
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> SET GLOBAL rpl_semi_sync_master_enabled = 1;
從數據庫執行:
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
以上的啓動方式是在登陸mysql後的命令行操做,也可寫在my.cnf配置文件中(推薦這種啓動方式)。
主數據庫的my.cnf配置文件中添加:
plugin-load=rpl_semi_sync_master=semisync_master.so rpl_semi_sync_master_enabled=1
從數據庫的my.cnf配置文件中添加:
plugin-load=rpl_semi_sync_slave=semisync_slave.so rpl_semi_sync_slave_enabled=1
在個別高可用架構下,master和slave需同時啓動,以便在切換後能繼續使用半同步複製!即在主從數據庫的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
- 重啓從數據庫上的IO線程
mysql> STOP SLAVE IO_THREAD; mysql> START SLAVE IO_THREAD;
特別注意: 若是沒有重啓,則默認的仍是異步複製模式!,重啓後,slave會在master上註冊爲半同步複製的slave角色。這時候,主的error.log中會打印以下信息:
2019-01-05T10:03:40.104327Z 5 [Note] While initializing dump thread for slave with UUID <ce9aaf22-5af6-11e6-850b-000c2988bad2>, found a zombie dump thread with the same UUID. Master is killing the zombie dump thread(4). 2019-01-05T10:03:40.111175Z 4 [Note] Stop asynchronous binlog_dump to slave (server_id: 2) 2019-01-05T10:03:40.119037Z 5 [Note] Start binlog_dump to master_thread_id(5) slave_server(2), pos(mysql-bin.000003, 621) 2019-01-05T10:03:40.119099Z 5 [Note] Start semi-sync binlog_dump to slave (server_id: 2), pos(mysql-bin.000003, 621)
- 查看半同步是否在運行
主數據庫:
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> show status like 'Rpl_semi_sync_slave_status'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.20 sec)
這兩個變量經常使用來監控主從是否運行在半同步複製模式下。至此,MySQL半同步複製環境就部署完成了!
須要注意下,其實Mysql半同步複製並非嚴格意義上的半同步複製。當半同步複製發生超時時(由rpl_semi_sync_master_timeout參數控制,單位是毫秒,默認爲10000,即10s),會暫時關閉半同步複製,轉而使用異步複製。當master dump線程發送完一個事務的全部事件以後,若是在rpl_semi_sync_master_timeout內,收到了從庫的響應,則主從又從新恢復爲半同步複製。[一旦有一次超時自動降級爲異步].
mysql> show variables like "rpl_semi_sync_master_timeout"; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | rpl_semi_sync_master_timeout | 10000 | +------------------------------+-------+ 1 row in set (0.01 sec)
接下來能夠測試下:
1) 主數據庫 (從數據庫在執行"stop slave"以前)
mysql> create database bobo; Query OK, 1 row affected (0.05 sec) mysql> create table bobo.ceshi(id int); Query OK, 0 row affected (0.28 sec) mysql> insert into bobo.ceshi values(1); Query OK, 1 row affected (0.09 sec)
2) 從數據執行"stop slave"
mysql> stop slave;
再觀察主數據庫
mysql> insert into bobo.ceshi values(2); Query OK, 1 row affected (10.01 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> show status like 'Rpl_semi_sync_slave_status'; +-----------------------------+-------+ | Variable_name | Value | +-----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF | +-----------------------------+-------+ 1 row in set (0.01 sec)
3) 接着再在從數據庫執行"start slave"
mysql> start slave;
再觀察主數據
mysql> insert into bobo.ceshi values(3); Query OK, 1 row affected (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> show status like 'Rpl_semi_sync_slave_status'; +-----------------------------+-------+ | Variable_name | Value | +-----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +-----------------------------+-------+ 1 row in set (0.00 sec)
以上驗證分爲三個階段:
1) 在Slave執行stop slave以前,主的insert操做很快就能返回。
2) 在Slave執行stop slave後,主的insert操做須要10.01s才返回,而這與rpl_semi_sync_master_timeout參數的時間相吻合。這時,查看兩個狀態的值,均爲「OFF」了。同時,主的error.log中打印以下信息:
2019-01-05T11:51:49.855452Z 6 [Warning] Timeout waiting for reply of binlog (file: mysql-bin.000003, pos: 1447), semi-sync up to file mysql-bin.000003, position 1196. 2019-01-05T11:51:49.855742Z 6 [Note] Semi-sync replication switched OFF.
3) 在Slave執行start slave後,主的insert操做很快就能返回,此時,兩個狀態的值也變爲「ON」了。同時,主的error.log中會打印以下信息:
2019-01-05T11:52:40.477098Z 7 [Note] Start binlog_dump to master_thread_id(7) slave_server(2), pos(mysql-bin.000003, 1196) 2019-01-05T11:52:40.477168Z 7 [Note] Start semi-sync binlog_dump to slave (server_id: 2), pos(mysql-bin.000003, 1196) 2019-01-05T11:52:40.523475Z 0 [Note] Semi-sync replication switched ON at (mysql-bin.000003, 1447)
其餘變量說明
環境變量(show variables like '%Rpl%';)
mysql> show variables like '%Rpl%'; +-------------------------------------------+------------+ | 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 | | rpl_stop_slave_timeout | 31536000 | +-------------------------------------------+------------+ 7 rows in set (0.30 sec)
rpl_semi_sync_master_wait_for_slave_count
MySQL 5.7.3引入的,該變量設置主須要等待多少個slave應答,才能返回給客戶端,默認爲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。
OFF
當狀態變量Rpl_semi_sync_master_clients中的值於rpl_semi_sync_master_wait_for_slave_count時,Rpl_semi_sync_master_status當即顯示爲OFF,即異步複製。
簡單來講,若是mysql架構是1主2從,2個從都採用了半同步複製,且設置的是rpl_semi_sync_master_wait_for_slave_count=2,若是其中一個掛掉了,對於rpl_semi_sync_master_wait_no_slave設置爲ON的狀況,此時顯示的仍然是半同步複製,若是rpl_semi_sync_master_wait_no_slave設置爲OFF,則會馬上變成異步複製。
狀態變量(show status like '%Rpl_semi%';)
mysql> show 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 | 6 | | 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 | 1120 | | Rpl_semi_sync_master_tx_wait_time | 4483 | | 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 | +--------------------------------------------+-------+ 14 rows in set (0.00 sec)
Rpl_semi_sync_master_clients
當前半同步複製從的個數,若是是一主多從的架構,並不包含異步複製從的個數。
Rpl_semi_sync_master_no_tx
The number of commits that were not acknowledged successfully by a slave.
具體到上面的測試中,指的是insert into bobo.ceshi values(2)這個事務。
Rpl_semi_sync_master_yes_tx
The number of commits that were acknowledged successfully by a slave.
具體到上面的測試中,指的是如下四個事務:
mysql> create database bobo;
mysql> create table bobo.ceshi(id int);
mysql> insert into bobo.ceshi values(1);
mysql> insert into bobo.ceshi values(3);
簡單總結:
1) 在一主多從的架構中,若是要開啓半同步複製,並不要求全部的從都是半同步複製。
2) MySQL 5.7極大的提高了半同步複製的性能。
5.6版本的半同步複製,dump thread 承擔了兩份不一樣且又十分頻繁的任務:傳送binlog 給slave ,還須要等待slave反饋信息,並且這兩個任務是串行的,dump thread 必須等待 slave 返回以後纔會傳送下一個 events 事務。dump thread 已然成爲整個半同步提升性能的瓶頸。在高併發業務場景下,這樣的機制會影響數據庫總體的TPS 。
5.7版本的半同步複製中,獨立出一個 ack collector thread ,專門用於接收slave 的反饋信息。這樣master 上有兩個線程獨立工做,能夠同時發送binlog 到slave ,和接收slave的反饋。
Mysql半同步模式配置示例
mysql主數據庫: 172.16.60.205 mysql從數據庫: 172.16.60.206 mysql5.6.39 安裝部署,參考:https://www.cnblogs.com/kevingrace/p/6109679.html 主數據庫172.16.60.205配置: [root@mysql-master ~]# cat /usr/local/mysql/my.cnf ....... server-id=1 log-bin=mysql-bin sync_binlog = 1 binlog_checksum = none binlog_format = mixed 從數據庫172.16.60.206配置: [root@mysql-slave ~]# cat /usr/local/mysql/my.cnf ......... server-id=2 log-bin=mysql-bin slave-skip-errors = all 而後從庫同步操做,不須要跟master_log_file 和 master_log_pos=120 mysql> change master to master_host = '172.16.60.205', master_port = 3306, master_user ='slave', master_password ='slave@123'; 其餘的配置,參考https://www.cnblogs.com/kevingrace/p/6256603.html 即主從同步配置不過濾庫,是基於整個數據庫的同步。其餘的操做和這個文檔中記錄的差很少 ======================================================== 主數據庫 [root@mysql-master ~]# mysql -p123456 ...... mysql> use kevin; mysql> show tables; +-----------------+ | Tables_in_kevin | +-----------------+ | haha | +-----------------+ 1 row in set (0.00 sec) mysql> select * from haha; +----+--------+ | id | name | +----+--------+ | 2 | anxun | | 3 | huoqiu | | 4 | xihuju | +----+--------+ 3 rows in set (0.00 sec) 從數據庫: [root@mysql-slave ~]# mysql -p123456 .......... .......... mysql> show slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 172.16.60.205 Master_User: slave Master_Port: 3306 Connect_Retry: 60 ......... ......... Slave_IO_Running: Yes Slave_SQL_Running: Yes mysql> select * from kevin.haha; +----+--------+ | id | name | +----+--------+ | 2 | anxun | | 3 | huoqiu | | 4 | xihuju | +----+--------+ 3 rows in set (0.00 sec) 主數據庫插入數據 mysql> insert into haha values(1,"linux"); Query OK, 1 row affected (0.03 sec) mysql> insert into haha values(11,"linux-ops"); Query OK, 1 row affected (0.04 sec) 從數據庫查看 mysql> select * from kevin.haha; +----+-----------+ | id | name | +----+-----------+ | 1 | linux | | 2 | anxun | | 3 | huoqiu | | 4 | xihuju | | 11 | linux-ops | +----+-----------+ 5 rows in set (0.00 sec) mysql> select version(); +------------+ | version() | +------------+ | 5.6.39-log | +------------+ 1 row in set (0.00 sec) mysql> show variables like "have_dynamic_loading"; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | have_dynamic_loading | YES | +----------------------+-------+ 1 row in set (0.00 sec) 從上面可知,已知足mysql半同步複製功能部署的條件: 1)MySQL 5.5及以上版本! 2)變量have_dynamic_loading爲YES 3)主從複製已經存在! =================================================== 接下來進行mysql半同步複製環境部署: 1)加載mysql半同步複製的插件 主數據庫執行: mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; Query OK, 0 row affected (0.04 sec) 從數據庫執行: mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; Query OK, 0 row affected (0.04 sec) 2)查看插件是否加載成功的兩種方式: 主數據庫執行: 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> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; +----------------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +----------------------+---------------+ | rpl_semi_sync_slave | ACTIVE | +----------------------+---------------+ 2 rows in set (0.01 sec) 3) 啓動半同步複製 主數據庫執行: mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1; Query OK, 0 rows affected (0.00 sec) 從數據庫執行: mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1; Query OK, 0 rows affected (0.00 sec) ---------------------------------------------------------------------------------------------------------------------------------------------- 舒適提示: 除了上面的設置方法之外, 還能夠如下面方式啓動半同步複製,即在my.cnf文件中添加啓動配置(推薦這種方式): 主數據庫 [root@mysql-master ~]# vim /usr/local/mysql/my.cnf #在[mysqld]區域添加下面內容 ........ plugin-load=rpl_semi_sync_master=semisync_master.so rpl_semi_sync_master_enabled=ON #或者設置爲"1",即開啓半同步複製功能 rpl-semi-sync-master-timeout=1000 #超時時間爲1000ms,即1s [root@mysql-master ~]# /etc/init.d/mysqld restart 主數據庫 [root@mysql-slave ~]# vim /usr/local/mysql/my.cnf ........ plugin-load=rpl_semi_sync_slave=semisync_slave.so rpl_semi_sync_slave_enabled=ON [root@mysql-slave ~]# /etc/init.d/mysqld restart ---------------------------------------------------------------------------------------------------------------------------------------------- 4)查看半同步是否在運行 主數據庫執行: 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) 從數據庫執行(此時可能仍是OFF狀態,須要在下一步重啓IO線程後,從庫半同步狀態纔會爲ON): 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) 5)重啓從數據庫上的IO線程 mysql> STOP SLAVE IO_THREAD; Query OK, 0 rows affected (0.04 sec) mysql> START SLAVE IO_THREAD; Query OK, 0 rows affected (0.00 sec) 重啓從數據的IO線程以後, slave會在master上註冊爲半同步複製的slave角色 查看主數據庫上的error日誌,就會發現下面信息: [root@mysql-master ~]# tail -f /data/mysql/data/mysql-error.log 2019-01-06 23:23:34 10436 [Note] Semi-sync replication initialized for transactions. 2019-01-06 23:23:34 10436 [Note] Semi-sync replication enabled on the master. 2019-01-06 23:27:28 10436 [Note] Stop asynchronous binlog_dump to slave (server_id: 2) 2019-01-06 23:27:28 10436 [Note] Start semi-sync binlog_dump to slave (server_id: 2), pos(mysql-bin.000004, 2944) 2019-01-06 23:32:03 10436 [Note] Stop semi-sync binlog_dump to slave (server_id: 2) 2019-01-06 23:32:03 10436 [Note] Start semi-sync binlog_dump to slave (server_id: 2), pos(mysql-bin.000004, 3357) 如上操做,就已經部署了mysql的半同步複製環境 如今往主數據庫插入新數據 mysql> insert into kevin.haha values(5,"grace"); Query OK, 1 row affected (0.13 sec) mysql> insert into kevin.haha values(6,"huihui"); Query OK, 1 row affected (0.11 sec) 到從數據庫上查看,正常複製過來了 mysql> show slave status \G; ......... ......... Slave_IO_Running: Yes Slave_SQL_Running: Yes mysql> select * from kevin.haha; +----+-----------+ | id | name | +----+-----------+ | 1 | linux | | 2 | anxun | | 3 | huoqiu | | 4 | xihuju | | 5 | grace | | 6 | huihui | | 11 | linux-ops | +----+-----------+ 7 rows in set (0.00 sec) 查看主數據庫 mysql> show status like '%Rpl_semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 475 | #網絡等待的平均時間 | Rpl_semi_sync_master_net_wait_time | 1427 | #網絡等待時間 | Rpl_semi_sync_master_net_waits | 3 | | Rpl_semi_sync_master_no_times | 0 | | Rpl_semi_sync_master_no_tx | 0 | #大於0就是異步。半同步是應爲0 | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 622 | # 平均等待時間 | Rpl_semi_sync_master_tx_wait_time | 1868 | #總的等待時間 | 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 | #大於0就是半同步模式 +--------------------------------------------+-------+ 14 rows in set (0.00 sec) ========================================================== 如今作下測試: 當半同步複製發生超時(由rpl_semi_sync_master_timeout參數控制,單位是毫秒,默認爲10000,即10s),會暫時關閉半同步複製,轉而使用異步複製。 當master dump線程發送完一個事務的全部事件以後,若是在rpl_semi_sync_master_timeout內,收到了從庫的響應,則主從又從新恢復爲半同步複製。 mysql> show variables like "rpl_semi_sync_master_timeout"; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | rpl_semi_sync_master_timeout | 10000 | +------------------------------+-------+ 1 row in set (0.00 sec) 關閉從數據庫的slave mysql> stop slave; Query OK, 0 rows affected (0.14 sec) 而後在主數據庫插入一條數據,發現半同步複製會發生超時 發生超時後,暫時關閉半同步複製,轉而使用異步複製 mysql> insert into kevin.haha values(55,"laolao"); Query OK, 1 row affected (10.13 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> show status like 'Rpl_semi_sync_slave_status'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF | +----------------------------+-------+ 1 row in set (0.00 sec) 接着再打開從數據庫的slave mysql> start slave; Query OK, 0 rows affected (0.04 sec) 而後主數據再插入一條數據,就會發現半同步複製不會超時,半同步複製功能打開也打開了 mysql> insert into kevin.haha values(555,"laolaolao"); Query OK, 1 row affected (0.04 sec) mysql> select * from haha; +-----+-----------+ | id | name | +-----+-----------+ | 1 | linux | | 2 | anhui | | 3 | huoqiu | | 4 | xihuju | | 5 | grace | | 6 | huihui | | 11 | linux-ops | | 55 | laolao | | 555 | laolaolao | +-----+-----------+ 9 rows 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> select * from haha; +-----+-----------+ | id | name | +-----+-----------+ | 1 | linux | | 2 | anhui | | 3 | huoqiu | | 4 | xihuju | | 5 | grace | | 6 | huihui | | 11 | linux-ops | | 55 | laolao | | 555 | laolaolao | +-----+-----------+ 9 rows in set (0.00 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)
經過上面的驗證可知,遇到半同步複製超時狀況,就會自動降爲異步工做。能夠在Slave上停掉半同步協議,而後在Master上建立數據庫看一下能不能複製到Slave上。須要注意一點的是,當Slave開啓半同步後,或者當主從之間網絡延遲恢復正常的時候,半同步複製會自動從異步複製又轉爲半同步複製,仍是至關智能的。
刪除"半同步複製", 恢復"異步複製"模式
先在從數據庫上關閉半同步複製功能,而後卸載半同步插件 mysql> stop slave; Query OK, 0 rows affected (0.07 sec) mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 0; Query OK, 0 rows affected (0.00 sec) mysql> show status like 'Rpl_semi_sync_slave_status'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF | +----------------------------+-------+ 1 row in set (0.00 sec) mysql> UNINSTALL PLUGIN rpl_semi_sync_slave; Query OK, 0 rows affected (0.00 sec) mysql> show status like 'Rpl_semi_sync_slave_status'; Empty set (0.00 sec) mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; Empty set (0.01 sec) 刪除my.cnf裏面的半同步配置 [root@mysql-slave mysql]# vim /usr/local/mysql/my.cnf #plugin-load=rpl_semi_sync_slave=semisync_slave.so #rpl_semi_sync_slave_enabled=ON 必定要重啓mysql服務 [root@mysql-slave mysql]# /etc/init.d/mysql restart Shutting down MySQL.. [ OK ] Starting MySQL.. [ OK ] ========================================================= 接着再主數據庫關閉半同步複製功能,卸載半同步插件 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> SET GLOBAL rpl_semi_sync_master_enabled = 0; Query OK, 0 rows affected (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> show status like '%Rpl_semi%'; +--------------------------------------------+--------+ | Variable_name | Value | +--------------------------------------------+--------+ | Rpl_semi_sync_master_clients | 0 | #確保這一行的數值爲0,即沒有半同步複製的客戶端 | Rpl_semi_sync_master_net_avg_wait_time | 40186 | | Rpl_semi_sync_master_net_wait_time | 401860 | | Rpl_semi_sync_master_net_waits | 10 | | Rpl_semi_sync_master_no_times | 2 | | Rpl_semi_sync_master_no_tx | 1 | | Rpl_semi_sync_master_status | OFF | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 592 | | Rpl_semi_sync_master_tx_wait_time | 4737 | | Rpl_semi_sync_master_tx_waits | 8 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 8 | +--------------------------------------------+--------+ 14 rows in set (0.00 sec) mysql> UNINSTALL PLUGIN rpl_semi_sync_master; Query OK, 0 rows affected (0.00 sec) mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; Empty set (0.00 sec) mysql> show status like 'Rpl_semi_sync_master_status'; Empty set (0.00 sec) 刪除my.cnf裏面的半同步配置 [root@mysql-master ~]# vim /usr/local/mysql/my.cnf #plugin-load=rpl_semi_sync_master=semisync_master.so #rpl_semi_sync_master_enabled=ON #rpl-semi-sync-master-timeout=1000 必定要重啓mysql服務 [root@mysql-master ~]# /etc/init.d/mysql restart Shutting down MySQL... [ OK ] Starting MySQL.. [ OK ] ========================================================= 再接着重啓從數據庫上的IO線程 mysql> STOP SLAVE IO_THREAD; Query OK, 0 rows affected (0.04 sec) mysql> START SLAVE IO_THREAD; Query OK, 0 rows affected (0.00 sec) ========================================================= 經過上面操做,就徹底刪掉了mysql半同步複製模式,恢復異步複製模式。 在關閉半同步複製模式的過程當中,能夠查看/data/mysql/data/mysql-error.log日誌信息,從中觀察到半同步關閉的信息。 在從機從新start slave mysql> start slave; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> show slave status \G; ......... ......... Slave_IO_Running: Yes Slave_SQL_Running: Yes 接着在主數據插入新數據 mysql> select * from kevin.haha; +-----+-----------+ | id | name | +-----+-----------+ | 1 | linux | | 2 | anhui | | 3 | huoqiu | | 4 | xihuju | | 5 | grace | | 6 | huihui | | 11 | linux-ops | | 55 | laolao | | 555 | laolaolao | +-----+-----------+ 9 rows in set (0.00 sec) mysql> insert into kevin.haha values(12,"wangjuan"); Query OK, 1 row affected (0.04 sec) mysql> insert into kevin.haha values(13,"congcong"); Query OK, 1 row affected (0.04 sec) 去從庫上查看,發現已經同步過來了 mysql> select * from kevin.haha; +-----+-----------+ | id | name | +-----+-----------+ | 1 | linux | | 2 | anhui | | 3 | huoqiu | | 4 | xihuju | | 5 | grace | | 6 | huihui | | 11 | linux-ops | | 12 | wangjuan | | 13 | congcong | | 55 | laolao | | 555 | laolaolao | +-----+-----------+ 9 rows in set (0.00 sec) ====================================================== 舒適提示: 通過幾回實驗,發現了一個坑,就是在作mysql半同步複製模式下,主從同步配置要是基於整個數據庫的同步,而不要使用"binlog_do_db" 和"binlog_ingore_db"進行過濾庫的同步配置,不然會形成半同步複製模式下的數據同步失敗。 以前作過一次實驗,mysql主從關係是針對某個具體庫進行配置同步的,即: 主數據庫的my.cnf配置: #主從同步配置 server-id=1 log-bin=mysql-bin binlog-do-db=kevin binlog-ignore-db=mysql sync_binlog = 1 binlog_checksum = none binlog_format = mixed # 半同步複製功能開啓 plugin-load=rpl_semi_sync_master=semisync_master.so rpl_semi_sync_master_enabled=ON rpl-semi-sync-master-timeout=1000 從數據庫的my.cnf配置: #主從同步配置 server-id=2 log-bin=mysql-bin replicate-do-db=kevin replicate-ignore-db=mysql slave-skip-errors = all # 半同步複製功能開啓 plugin-load=rpl_semi_sync_slave=semisync_slave.so rpl_semi_sync_slave_enabled=ON 而後從庫經過下面方式跟主數據庫同步 mysql> change master to master_host='172.16.60.205,master_user='slave',master_password='slave@123',master_log_file='mysql-bin.000007',master_log_pos=120; 以上配置的mysql主從同步是針對kevin這個具體的庫的,在後續半同步複製模式下,主從數據同步失敗。 並且在這種狀況下,刪除半同步複製模式配置,恢復到異步同步模式,主從數據同步仍是失敗。 ----------------------------------------------------------- 最後修改主從數據庫的my.cnf文件,按照下面方式進行主從同步,則半同步複製模式下,主從數據同步正常。 而且刪除半同步複製模式配置,恢復到異步同步模式,主從數據同步同樣正常。 主數據庫的my.cnf文件調整後的配置: #主從同步配置 server-id=1 log-bin=mysql-bin sync_binlog = 1 binlog_checksum = none binlog_format = mixed # 半同步複製功能開啓 plugin-load=rpl_semi_sync_master=semisync_master.so rpl_semi_sync_master_enabled=1 rpl-semi-sync-master-timeout=1000 從數據庫的my.cnf文件調整後的配置: #主從同步配置 server-id=2 log-bin=mysql-bin slave-skip-errors = all # 半同步複製功能開啓 plugin-load=rpl_semi_sync_slave=semisync_slave.so rpl_semi_sync_slave_enabled=1 而後從庫經過下面方式跟主數據庫同步 mysql> change master to master_host = '172.16.60.205', master_port = 3306, master_user ='slave', master_password ='slave@123'; ============================================================ 特別注意下: 在作mysq主從同步時最好別過濾庫了,即最好進行基於整個數據庫的同步配置,同步命令爲: "change master to master_host = '主數據庫ip', master_port = 主數據庫mysql端口, master_user ='同步的用戶', master_password ='同步的密碼';" 使用過濾庫或過濾表的方式進行主從同步配置,後續會帶來一些比較麻煩的坑。 若是業務數比較多的狀況下,就使用mysql多實例方式進行同步,一個業務一個mysql實例,主從同步配置中不要進行過濾庫或表的配置,即基於整個數據庫同步。
另外,在實際使用中還碰到一種狀況從庫IO線程有延遲時,主庫會自動把半同步複製降爲異步複製;當從庫IO延遲沒有時,主庫又會把異步複製升級爲半同步複製。能夠進行壓測模擬,可是此時查看Master的狀態跟上面直接關閉Slave半同步有些不一樣,會發現Rpl_semi_sync_master_clients仍然等於1,而Rpl_semi_sync_master_status等於OFF。隨着MySQL 5.7版本的發佈,半同步複製技術升級爲全新的Loss-less Semi-Synchronous Replication架構,其成熟度、數據一致性與執行效率獲得顯著的提高。
MySQL 5.7半同步複製的改進
如今咱們已經知道,在半同步環境下,主庫是在事務提交以後等待Slave ACK,因此纔會有數據不一致問題。因此這個Slave ACK在什麼時間去等待,也是一個很關鍵的問題了。所以MySQL針對半同步複製的問題,在5.7.2引入了Loss-less Semi-Synchronous,在調用binlog sync以後,engine層commit以前等待Slave ACK。這樣只有在確認Slave收到事務events後,事務纔會提交。在commit以前等待Slave ACK,同時能夠堆積事務,利於group commit,有利於提高性能。
MySQL 5.7安裝半同步模塊,命令以下:
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; Query OK, 0 rows affected (0.00 sec)
看一下相關狀態信息:
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 | +-------------------------------------------+------------+ 6 rows in set (0.00 sec)
支持無損複製(Loss-less Semi-Synchronous)
在Loss-less Semi-Synchronous模式下,master在調用binlog sync以後,engine層commit以前等待Slave ACK(須要收到至少一個Slave節點回復的ACK後)。這樣只有在確認Slave收到事務events後,master事務纔會提交,而後把結果返回給客戶端。此時此事務纔對其餘事務可見。在這種模式下解決了after_commit模式帶來的幻讀和數據丟失問題,由於主庫沒有提交事務。但也會有個問題,假設主庫在存儲引擎提交以前掛了,那麼很明顯這個事務是不成功的,但因爲對應的Binlog已經作了Sync操做,從庫已經收到了這些Binlog,而且執行成功,至關於在從庫上多了數據,也算是有問題的,但多了數據,問題通常不算嚴重。這個問題能夠這樣理解,做爲MySQL,在沒辦法解決分佈式數據一致性問題的狀況下,它能保證的是不丟數據,多了數據總比丟數據要好。
無損複製其實就是對semi sync增長了rpl_semi_sync_master_wait_point參數,來控制半同步模式下主庫在返回給會話事務成功以前提交事務的方式。rpl_semi_sync_master_wait_point該參數有兩個值:AFTER_COMMIT和AFTER_SYNC
第一個值:AFTER_COMMIT(5.6默認值)
master將每一個事務寫入binlog(sync_binlog=1),傳遞到slave刷新到磁盤(sync_relay=1),同時主庫提交事務。master等待slave反饋收到relay log,只有收到ACK後master纔將commit OK結果反饋給客戶端。
第二個值:AFTER_SYNC(5.7默認值,但5.6中無此模式)
master將每一個事務寫入binlog , 傳遞到slave刷新到磁盤(relay log)。master等待slave反饋接收到relay log的ack以後,再提交事務而且返回commit OK結果給客戶端。 即便主庫crash,全部在主庫上已經提交的事務都能保證已經同步到slave的relay log中。
半同步複製與無損複製的對比
1) ACK的時間點不一樣
- 半同步複製在InnoDB層的Commit Log後等待ACK,主從切換會有數據丟失風險。
- 無損複製在MySQL Server層的Write binlog後等待ACK,主從切換會有數據變多風險。
2) 主從數據一致性
- 半同步複製意味着在Master節點上,這個剛剛提交的事物對數據庫的修改,對其餘事物是可見的。所以,若是在等待Slave ACK的時候crash了,那麼會對其餘事務出現幻讀,數據丟失。
- 無損複製在write binlog完成後就傳輸binlog,但尚未去寫commit log,意味着當前這個事物對數據庫的修改,其餘事物也是不可見的。所以,不會出現幻讀,數據丟失風險。
所以5.7引入了無損複製(after_sync)模式,帶來的主要收益是解決after_commit致使的master crash後數據丟失問題,所以在引入after_sync模式後,全部提交的數據已經都被複制,故障切換時數據一致性將獲得提高。
- 性能提高,支持發送binlog和接受ack的異步化
舊版本的semi sync受限於dump thread ,緣由是dump thread承擔了兩份不一樣且又十分頻繁的任務:傳送binlog給slave ,還須要等待slave反饋信息,並且這兩個任務是串行的,dump thread必須等待slave返回以後纔會傳送下一個events事務。dump thread已然成爲整個半同步提升性能的瓶頸。在高併發業務場景下,這樣的機制會影響數據庫總體TPS 。
可是在MySQL 5.7.17以前,這個Ack Receiver線程採用了select機制來監聽slave返回的結果,然而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句柄數理論上沒有上限了,緣由是它是基於鏈表來存儲的。可是一樣有缺點:好比大量的fd的數組被總體複製於用戶態和內核地址空間之間,而無論這樣的複製是否是有意義。poll還有一個特色是「水平觸發」,若是報告了fd後,沒有被處理,那麼下次poll時會再次報告該fd。
其實在高性能軟件中都是用另一種調用機制,名爲epoll,高性能的表明,好比Nginx,haproxy等都是使用epoll。可能poll的複雜性比epoll低,另外對於ack receiver線程來講可能poll足矣。
- 性能提高,控制主庫接收slave寫事務成功反饋數量
MySQL 5.7新增了rpl_semi_sync_master_wait_slave_count參數,能夠用來控制主庫接受多少個slave寫事務成功反饋,給高可用架構切換提供了靈活性。如圖所示,當count值爲2時,master需等待兩個slave的ack。
- 性能提高, Binlog互斥鎖改進
舊版本半同步複製在主提交binlog的寫會話和dump thread讀binlog的操做都會對binlog添加互斥鎖,致使binlog文件的讀寫是串行化的,存在併發度的問題。
MySQL 5.7對binlog lock進行了如下兩方面優化:
- 移除了dump thread對binlog的互斥鎖。
- 加入了安全邊際保證binlog的讀安全。
從replication功能引入後,官方MySQL一直在不停完善,但也能夠發現當前原生的MySQL主備複製的實現,實際上很難在知足數據一致性前提下作到高可用高性能。
參數sync_binlog/sync_relay與半同步複製
sync_binlog的配置
其實無損複製流程中也會存在着會致使主備數據不一致的狀況,使主備同步失敗的情形。
當sync_binlog爲0的時候,binlog sync磁盤由操做系統負責。當不爲0的時候,其數值爲按期sync磁盤的binlog commit group數。sync_binlog值不等於1的時候事務在FLUSH階段就傳輸binlog到從庫了,而值爲1時,binlog同步操做是在SYNC階段後。當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的狀況。流程以下圖所示。Master依次執行flush binlog, update binlog position, sync binlog。若是Master在update binlog position後,sync binlog前掉電,Master再次啓動後原事務就會被回滾。但可能出現Slave獲取到Events,這也會致使Slave數據比Master多,主備同步失敗。
因爲上面的緣由,sync_binlog設置爲1的時候,MySQL會update binlog end pos after sync。流程以下圖所示。這時候,對於每個事務都須要sync binlog,同時sync binlog和網絡發送events會是一個串行的過程,性能降低明顯。
在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就會發現數據丟失。
經過上面這個Case,MySQL semisync若是要保證任意時刻發生一臺機器宕機都不丟失數據,須要同時設置sync_relay_log爲1。對relay log的sync操做是在queue_event中,對每一個event都要sync,因此sync_relay_log設置爲1的時候,事務響應時間會受到影響,對於涉及數據比較多的事務延遲會增長不少。
MySQL三節點
在一主一從的主備semisync的數據一致性分析中放棄了高可用,當主備之間網絡抖動或者一臺宕機的狀況下中止提供服務。要作到高可用,很天然咱們能夠想到一主兩從,這樣解決某一網絡抖動或一臺宕機時候的可用性問題。可是,前文敘述要保證數據一致性配置要求依然存在,即正常狀況下的性能不會有改善。同時須要解決Master宕機時候,如何選取新主機的問題,如何避免多主的情形。
選取新主機時必定要讀取兩個從機,看哪個從機有最新的日誌,不然可能致使數據丟失。這樣的三節點方案就相似分佈式Quorum機制,寫的時候須要保證寫成功三節點中的法定集合,肯定新主的時候須要讀取法定集合。利用分佈式一致性協議Paxos/Raft能夠解決數據一致性問題,選主問題和多主問題,所以近些年,國內數據庫團隊大多實現了基於Paxos/Raft的三節點方案。MySQL官方後續也以插件形式引入了支持多主集羣的Group Replication方案。