一、GTID的概述:
1、全局事物標識:global transaction identifieds。
2、GTID事物是全局唯一性的,且一個事務對應一個GTID。
3、一個GTID在一個服務器上只執行一次,避免重復執行導致數據混亂或者主從不一致。
4、GTID用來代替classic的復制方法,不在使用binlog+pos開啟復制。而是使用master_auto_postion=1的方式自動匹配GTID斷點進行復制。
5、MySQL-5.6.5開始支持的,MySQL-5.6.10后開始完善。
6、在傳統的slave端,binlog是不用開啟的,但是在GTID中,slave端的binlog是必須開啟的,目的是記錄執行過的GTID(強制)。
二、GTID的組成部分:
前面是server_uuid:后面是一個序列號
例如:server_uuid:sequence number
7800a22c-95ae-11e4-983d-080027de205a:10
UUID:每個mysql實例的唯一ID,由於會傳遞到slave,所以也可以理解為源ID。
Sequence number:在每台MySQL服務器上都是從1開始自增長的序列,一個數值對應一個事務。
三、GTID比傳統復制的優勢:
1、更簡單的實現failover,不用以前那樣在需要找log_file和log_Pos。
2、更簡單的搭建主從復制。
3、比傳統復制更加安全。
4、GTID是連續沒有空洞的,因此主從庫出現數據沖突時,可以用添加空事物的方式進行跳過。
四、GTID的工作原理:
要點:
1、slave在接受master的binlog時,會校驗master的GTID是否已經執行過(一個服務器只能執行一次)。
2、為了保證主從數據的一致性,多線程只能同時執行一個GTID。
六、使用GTID搭建mysql的主從復制的主要參數:
注意:建議使用mysql-5.6.5以上的最新版本。
(二)、啟動GTID的兩種方法:
方法一、
1、如果是在已經跑的服務器,你需要重啟一下mysql server。
2、啟動之前,一定要先關閉master的寫入,保證所有slave端都已經和master端數據保持同步。
3、所有slave需要加上skip_slave_start=1的配置參數,避免啟動后還是使用老的復制協議。
方法二、
1、如果是新搭建的服務器,直接啟動就行了。
七、master-slave搭建的注意事項:
(一)、使用GTID的方式,把salve端掛載master端:
1、啟動以后最好不要立即執行事務,而是先change master上。
2、然后在執行事務,當然知不是必須的。
3、使用下面的sql切換slave到新的master。
stop slave;
CHANGE MASTER TO
MASTER_HOST='127.0.0.1',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='repl',
master_auto_position = 1;
(二)、如果給已經運行的GTID的master端添加一個新的slave
有兩種方法:
方法一、適用於master也是新建不久的情況。
1、如果你的master所有的binlog還在。可以選擇類似於上面的方法,安裝slave,直接change master to到master端。
2、原理是直接獲取master所有的GTID並執行。
3、優點:簡單方便。
4、缺點:如果binlog太多,數據完全同步需要時間較長,並且master一開始就啟用了GTUD。
方法二、適用於擁有較大數據的情況。(推薦)
1、通過master或者其他slave的備份搭建新的slave。(看第三部分)
2、原理:獲取master的數據和這些數據對應的GTID范圍,然后通過slave設置@@global.gtid_purged跳過備份包含的gtid。
3、優點:是可以避免第一種方法的不足。
4、缺點:相對來說有點復雜。
(三)、通過備份搭建新的slave:(方法二的擴展)
兩種方法:
方法一、mysqldump的方式:
1、在備份的時候指定--master-data=2(來保存binlog的文件號和位置的命令)。
2、使用mysqldump的命令在dump文件里可以看到下面兩個信息:
SET @@SESSION.SQL_LOG_BIN=0;
SET @@GLOBAL.GTID_PURGED='7800a22c-95ae-11e4-983d-080027de205a:1-8';
3、將備份還原到slave后,使用change master to命令掛載master端。
注意:在mysql5.6.9以后的命令才支持這個功能。
方法二、percona Xtrabackup
1、Xtrabackup_binlog_info文件中,包含global.gtid_purged='XXXXXX:XXXX'的信息。
2、然后到slave去手工的 SET GLOBAL.GTID_PURGED='XXXXXX:XXXX'。
3、恢復備份,開啟change master to 命令。
注意:如果系統運行了很久,無法找到GTID的變好了,可以通過上面的方式進行查找。
八、GTID如何跳過事務沖突:
1、這個功能主要跳過事務,代替原來的set global sql_slave_skip_counter = 1。
2、由於在這個GTID必須是連續的,正常情況同一個服務器產生的GTID是不會存在空缺的。所以不能簡單的skip掉一個事務,只能通過注入空事物的方法替換掉一個實際操作事務。
3、注入空事物的方法:
stop slave;
set gtid_next='xxxxxxx:N';
begin;commit;
set gtid_next='AUTOMAIC';
start slave;
4、這里的xxxxx:N 也就是你的slave sql thread報錯的GTID,或者說是你想要跳過的GTID。
在從庫上執行以下SQL:
mysql> set gtid_next='e10c75be-5c1b-11e6-ab7c-000c296078ae:6';
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set gtid_next='AUTOMATIC';
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.02 sec)
設置gtid_next的方法一次只能跳過一個事務,要批量的跳過事務可以通過設置gtid_purged完成。
GTID與crash safe salve
crash safe slave是MySQL 5.6提供的功能,意思是說在slave crash后,把slave重新拉起來可以繼續從Master進行復制,不會出現復制錯誤也不會出現數據不一致。
1、基於binlog文件位置的復制
在基於binlog文件位置的復制下,要保證crash safe slave,配置下面的參數即可。
relay_log_info_repository = TABLE
relay_log_recovery = ON
這樣可行的原因是,relay_log_info_repository = TABLE時,apply event和更新relay_log_info表的操作被包含在同一個事務里,innodb要么讓它們同時生效,要么同時不生效,保證位點信息和已經應用的事務精確匹配。同時relay_log_recovery = ON時,會拋棄master_log_info中記錄的復制位點,根據relay_log_info的執行位置重新從Master獲取binlog,這就回避了由於未同步刷盤導致的binlog文件接受位置和實際不一致以及relay log文件被截斷的問題。
在同時使用MTS(multi-threaded slave)時,為保證crash safe slave基於binlog文件位置的復制還需要設置sync_relay_log=1,因為MySQL在Crash恢復時必須先通過讀取relay log補齊MTS導致的事務空洞。
2、基於GTID的復制
上面的設置並不適用於基於GTID的復制。在基於GTID的復制下,crash的Slave重啟后,從binlog中解析的gtid_executed決定了要apply哪些binlog記錄,所以binlog必須和innodb存儲引擎的數據保持一致。要做到這一點,需要把sync_binlog和innodb_flush_log_at_trx_commit都設置為1,即所謂的"雙1"。
另外MySQL啟動時,會從relay log文件中獲取已接收的GTIDs並更新Retrieved_Gtid_Set。由於relay log文件可能不完整,所以需要拋棄已接收的relay log文件。因此relay_log_recovery = ON也是必須的。
這樣,對於基於GTID的復制,保證crash safe slave的設置就是下面這樣。
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
relay_log_recovery = ON
關於如何設置以確保crash safe slave,官方文檔有明確記載,見 17.3.2 Handling an Unexpected Halt of a Replication Slave。
但是其中關於GTID的記載中存在筆誤,將relay_log_recovery=1寫成了relay_log_recovery=0 (#83711)。同時也沒有提到必須設置"雙1",但是"雙1"是必要的,否則crash的Slave重啟后,可能會重復應用binlog event也可能會遺漏應用binlog event(#70659)。其中遺漏應用binlog event的情況更可怕,因為Slave在不觸發SQL錯誤的情況下就默默的和Master不一致了。
3、設置"雙1"對性能的影響
出於安全考慮,強烈推薦設置"雙1"。"雙1"會增大每個事務的RT,但得益於MySQL的組提交機制,高並發下"雙1"對系統整體tps的影響在可接受范圍內。
sysbench oltp.lua 10張表每張表100w記錄(qps/並發數)
對更新同一行這樣無法有效並行的場景,"雙1"對性能的影響非常大。
sysbench update_non_index.lua 1張表1條記錄(qps/並發數)
對不能有效並行的Slave replay,存在同樣的問題。
通過指定tx-rate執行sysbench的update_non_index.lua腳本壓測30秒,完成后檢查主備延遲。
可以發現在Slave被配置為"雙1"的情況下,延遲非常嚴重,1000以上的QPS就會出現延遲,非"雙1"下QPS到5000以上才會出現延遲(主庫配置為"雙1")。
sysbench update_non_index.lua 1張表100w條記錄 128並發(延遲/qps)
以上測試環境是Percona Server 5.6運行在配置HDD的8 core虛機,由於測試結果和系統IO能力有很大關系,僅供參考。
4、如何在非"雙1"下保證crash safe slave
如果是MySQL 5.7可以關閉log_slave_updates,這樣MySQL會將已執行的GTIDs實時記錄到系統表mysql.gtid_executed中,mysql.gtid_executed是和用戶事務一起提交的,因此可以保證和實際的數據一致。
log_slave_updates = OFF
relay_log_recovery = ON
如果是MySQL 5.6可以采用如下變通的方式。
按照基於binlog文件復制時crash safe slave的要求設置relay_log_info_repository = TABLE
relay_log_info_repository = TABLE
relay_log_recovery = ON
在Slave crash后,根據relay_log_info_repository設置相應的gitd_purged再開啟復制,步驟如下。
1.啟動MySQL,但不開啟復制
mysqld --skip-slave-start
2.在Slave上修改為基於binlog文件位置的復制
change master to MASTER_AUTO_POSITION = 0
3.啟動slave IO線程
start slave io_thread
這里不能啟動SQL線程,如果接受到的GTID已經在Slave的gtid_executed里了,會被Slave skip掉。
4.檢查binlog傳輸的開始位置(即Retrieved_Gtid_Set的值)
show slave status\G
假設輸出的Retrieved_Gtid_Set值為e10c75be-5c1b-11e6-ab7c-000c296078ae:7-10
5.在Master上檢查gtid_executed
show master status
假設輸出的Executed_Gtid_Set值為e10c75be-5c1b-11e6-ab7c-000c296078ae:1-10
6.在Slave上設置gitd_purged為binlog傳輸位置的前面的GTID的集合
reset master;
set global gitd_purged='e10c75be-5c1b-11e6-ab7c-000c296078ae:1-6';
7.修改回auto position的復制
change master to MASTER_AUTO_POSITION = 1
8.啟動slave SQL線程
start slave sql_thread
但是,這種變通的方法不適合多線程復制。因為多線程復制可能產生gtid gap和Gap-free low-watermark position,這會導致Salve上重復apply已經apply過的event。后果就是數據不一致或者復制中斷,除非設置binlog格式為row模式並且slave_exec_mode=IDEMPOTENT,slave_exec_mode=IDEMPOTENT允許Slave回放binlog時忽略重復鍵和找不到鍵的錯誤,使得binlog回放具有冪等性,但這也意味着如果真的出現了主備數據不一致也會被它忽略。
5、MTS下特有的問題
在同時使用MTS(slave_parallel_workers > 1)時,即使按上面crash safe slave的要求設置了基於GTID的復制,Slave crash后再重啟還是會導致復制中斷。
通過強制殺掉MySQL所在虛機的方式模擬Slave宕機,然后再啟動MySQL,MySQL日志中有如下錯誤消息:
啟動slave時也會報錯
mysql> start slave;
ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository
出現這種現象的原因在於,relay_log_recovery=1 且 slave_parallel_workers>1的情況下,mysql啟動時會進入MTS Group恢復流程,即讀取relay log,嘗試填補由於多線程復制導致的gap。然后relay log文件由於不是實時刷新的,在relay log文件中找不到gap對應的relay log記錄(覆蓋了gap的relay log起始和結束位置分別被稱為低水位和高水位,低水位點即slave_relay_log_info.Relay_log_pos的值)就會報這個錯。
實際上,在GTID模式下,slave在apply event的時候可以跳過重復事件,所以可以安全的從低水位點應用日志,沒必要解析relay log文件。 這看上去是一個bug,於是提交了一個bug報告#83713,目前還沒有收到回復。
作為回避方法,可以通過清除relay log文件,跳過這個錯誤。執行步驟如下:
reset slave;
change master to MASTER_AUTO_POSITION = 1
start slave;
在這里,單純的調reset slave不能把狀態清理干凈,內部的Relay_log_info.inited標志位仍然處於未被初始化狀態,此時調用start slave仍然會失敗。因此需要補一刀change master。
6、Master的crash safe
前面一直在講crash safe slave,Master的crash safe同樣重要。 要想Master保持crash safe需要按下面的參數進行設置,否則不僅會丟失事務,gtid_executed還可能和實際的innodb存儲引擎中的數據不一致。
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
在Master配置為"雙1"的情況下,Master crash后,如果沒有發生failover,可以繼續作為Master。 如果發生了failover,可以檢查舊Master和新Master上由舊Master執行的事務集合是否一致。
show master status
如果一致,可以按MASTER_AUTO_POSITION = 1的方式將舊Master作為Slave和新Master建立復制關系。否則,考慮做事務補償或從新Master上拉取備份進行恢復。
在Master配置不是"雙1"的情況下,在Master crash后由於難以准確知道舊Master上究竟執行了哪些事務,安全的做法是實施主備切換,並從新Master上拉取備份,把舊Master作為新Master的Slave進行恢復。