MySQL 使用GTID進行復制
1. GTID的格式和存儲
GTID即全局事務ID(global transaction identifier),GTID實際上是由server_uuid:transaction_id
組成的。其中server_uuid是一個MySQL實例的唯一標識,存放在數據目錄的auto.cnf
文件下,transaction_id代表了該實例上已經提交的事務數量,並且隨着事務提交單調遞增,所以GTID能夠保證每個MySQL實例事務的執行(不會重復執行同一個事務,並且會補全沒有執行的事務)。
1.1 GTID 集
GTID集是一組全局事務標識符,如下所示:
gtid_set:
uuid_set [, uuid_set] ...
| ''
uuid_set:
uuid:interval[:interval]...
uuid:
hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
h:
[0-9|A-F]
interval:
n[-n]
(n >= 1)
GTID集在MySQL服務器中以多種方式使用。 例如,gtid_executed
和gtid_purged
系統變量存儲的值表示為GTID集。 此外,函數GTID_SUBSET()
和GTID_SUBTRACT()
需要GTID集作為輸入。 當從服務器變量返回GTID集時,UUID按字母順序排列,數值間隔按升序合並。
1.2 mysql.gtid_executed 表
GTID存儲在mysql數據庫中名為gtid_executed
的表中。 對於它表示的每個GTID或GTID集合,該表中的一行包含原始服務器的UUID,以及該集合的起始和結束事務ID; 對於僅引用單個GTID的行,這兩個最后兩個值是相同的。
當slave禁用binlog時,mysql.gtid_executed
表使slave能夠使用GTID,並且它可以在二進制日志丟失時保留GTID歷史記錄。
當gtid_mode為ON或ON_PERMISSIVE時,GTID僅存儲在mysql.gtid_executed表中,存儲GTID的位置取決於是啟用還是禁用binlog:
-
如果禁用二進制日志記錄(log_bin為OFF),或者如果禁用log_slave_updates,則服務器將屬於每個事務的GTID與表中的事務一起存儲。 此外,該表可以定期壓縮; 此情況僅不適用於復制中的master,因為在主服務器上,必須啟用二進制日志記錄才能進行復制。
-
如果啟用了二進制日志記錄(log_bin為ON),則無論何時輪詢二進制日志或關閉服務器,服務器都會將寫入先前二進制日志的所有事務的GTID寫入
mysql.gtid_executed
表。 這種情況適用於復制主服務器或啟用了二進制日志記錄的復制從服務器。如果服務器意外停止,則當前二進制日志中的GTID集不會保存在
mysql.gtid_executed
表中。 在這種情況下,這些GTID會在恢復期間添加到表和gtid_executed
系統變量中的GTID集合中。啟用二進制日志記錄時,
mysql.gtid_executed
表不會為所有已執行的事務提供GTID的完整記錄。 該信息由gtid_executed
系統變量的全局值提供。
命令RESET MASTER
將重置mysql.gtid_executed表。
1.3 mysql.gtid_executed 表壓縮
啟用GTID時,服務器會定期在mysql.gtid_executed
表上執行此類壓縮。 通過設置gtid_executed_compression_period
系統變量,您可以控制壓縮表之前允許的事務數,從而控制壓縮率。 該變量的默認值為1000; 這意味着,默認情況下,在每1000次事務之后執行表的壓縮。 將gtid_executed_compression_period設置為0可以防止執行壓縮; 但是,如果執行此操作,您應該為gtid_executed
表可能需要的磁盤空間量的大幅增加做好准備。
【注意】:
啟用binlog時,且不使用gtid_executed_compression_period的值,會在每個binlog輪換時壓縮mysql.gtid_executed表。
壓縮前:
mysql> SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 |
...
壓縮后:
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 43 |
...
mysql.gtid_executed
表的壓縮由名為thread/sql/compress_gtid_table
的專用前台線程執行。 該線程未在SHOW PROCESSLIST的輸出中列出,但可以將其視為performance_schema.threads
表中的一行,如下所示:
mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
THREAD_ID: 26
NAME: thread/sql/compress_gtid_table
TYPE: FOREGROUND
PROCESSLIST_ID: 1
PROCESSLIST_USER: NULL
PROCESSLIST_HOST: NULL
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
PROCESSLIST_TIME: 1509
PROCESSLIST_STATE: Suspending
PROCESSLIST_INFO: NULL
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: NULL
THREAD_OS_ID: 18677
thread/sql/compress_gtid_table
線程通常會休眠,直到執行了gtid_executed_compression_period
事務,然后喚醒以執行mysql.gtid_executed
表的壓縮,如前所述。 然后它休眠直到另一個gtid_executed_compression_period
事務發生,然后喚醒再次執行壓縮,無限期地重復此循環。 禁用二進制日志記錄時將此值設置為0意味着線程始終處於休眠狀態且從不喚醒。
2. GTID 生命周期
GTID的生命周期包括以下步驟:
- Master產生GTID:在Master端產生一個GTID的信息,並保存到binlog中。
- 發送Binlog信息到從庫上,將binlog信息發送到SLAVE所在的服務器上。將SLAVE中的gtid_next的值設置為GTID的值。
- SLAVE執行GTID。SLAVE首先驗證是否在自己的二進制日志中使用了這個GTID號
- SLAVE不生成GTID。由於GTID不為空,SLAVE不會嘗試為該事務生成新的GTID,而是從gtid_next 中讀取GTID值,寫入二進制日志中,來標識一個事務的GTID的值。
gtid_purged
gtid_purged
系統變量(@@global.gtid_purged
)中的GTID集包含已在服務器上提交但在服務器上的任何二進制日志文件中不存在的所有事務的GTID。 以下類別的GTID在此集合中:
- 在slave上禁用binlog提交的復制事務的GTID
- 已寫入已清除的binlog的事務的GTID
- 由語句
SET @@global.gtid_purged
明確添加到集合中的GTID
服務器啟動時,將初始化gtid_purged
系統變量中的GTID集。 每個二進制日志文件都以事件Previous_gtids_log_event開頭,該事件包含所有先前二進制日志文件中的GTID集(由前一個文件的Previous_gtids_log_event中的GTID和文件本身中每個Gtid_log_event的GTID組成)。 最舊的二進制日志文件中的Previous_gtids_log_event的內容用於在服務器啟動時初始化gtid_purged集,並在清除二進制日志文件時維護該集。
3. 使用GTID搭建主從
GTID使用master_auto_position=1
代替了基於binlog和position號的主從復制搭建方式,更便於主從復制的搭建。
3.1 環境准備
類型 | ip | prot | server-id | 是否開啟binlog | binlog格式 | log_slave_updates參數 |
---|---|---|---|---|---|---|
master | 192.168.56.100 | 3307 | 1003307 | log-bin = /data/mysql/mysql3307/logs/my3307_binlog | binlog_format = row | log_slave_updates=1 |
slave | 192.168.56.200 | 3307 | 2003307 | log-bin = /data/mysql/mysql3307/logs/my3307_binlog | binlog_format = row | log_slave_updates=1 |
3.2 配置GTID主從的參數
server_id
: 設置MySQL實例的server_id,每個server_id不能一樣gtid_mode=ON
: MySQL實例開啟GTID模式enforce_gtid_consitency=ON
:使用GTID模式復制時,需要開啟參數,用來保證數據的一致性。log-bin
: MySQL必須要開啟binloglog-slave-updates=1
:決定SLAVE從Master接收到更新且執行是否記錄到SLAVE的binlog中binlog_format=ROW
: binlog格式為rowskip-slave-start=1
(可選): 當SLAVE數據庫啟動的時候,SLAVE不會啟動復制
3.3 在master上操作
1) 創建復制賬號
create user 'repl'@'%' identified by 'wanbin'; grant replication slave on *.* to 'repl'@'%';
2) master數據庫利用xtrabackup備份至slave上
innobackupex --defaults-file=/etc/my3307.cnf -uroot -pmysql --stream=tar ./ |ssh root@mysqldb2 "cat - > /data/backup/dbback`date +%Y%m%d_%H%M%S`.tar"
3.4 在slave上操作
- 解壓備份
mkdir dbback20181012_151213
tar -xvf dbback20181012_151213.tar -C dbback20181012_151213
- prepare備份
innobackupex --apply-log /data/backup/dbback20181012_151213/
- 恢復備份
innobackupex --defaults-file=/etc/my3307.cnf --copy-back /data/backup/dbback20181012_151213/
chown -R mysql:mysql /data/mysql/mysql3307/data
mysqld --defaults-file=/etc/my3307.cnf &
- 過濾掉已執行過的gtid
1)查看xtrabackup_info文件中的gtid信息
cat xtrabackup_info|grep binlog_pos
binlog_pos = filename 'my3307_binlog.000002', position '2401', GTID of the last change '3a068bf8-cdeb-11e8-8176-080027b0b461:1-10'
2)查看slave已執行的gtid是否為空,如果不為空,需要執行reset MASTER進行清理,否則無法設置gtid。
root@localhost [(none)] 15:50:41>show master status \G;
*************************** 1. row ***************************
File: my3307_binlog.000002
Position: 234
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 3a068bf8-cdeb-11e8-8176-080027b0b461:1-10,
ffe86a27-cdef-11e8-bb92-0800275b8a9a:1-2
1 row in set (0.00 sec)
3)執行reset master
root@localhost [(none)] 15:50:52>reset master;
Query OK, 0 rows affected (0.04 sec)
root@localhost [(none)] 15:53:18>show master status\G
*************************** 1. row ***************************
File: my3307_binlog.000001
Position: 154
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)
4)執行GTID_PURGED
root@localhost [(none)] 15:56:32>set session sql_log_bin = 0;
root@localhost [(none)] 15:56:53>set global gtid_purged='3a068bf8-cdeb-11e8-8176-080027b0b461:1-10';
root@localhost [(none)] 15:57:58>set session sql_log_bin = 1;
- 配置主從
執行help change master to 可以獲取change master to命令
CHANGE MASTER TO
MASTER_HOST='192.168.56.100',
MASTER_USER='repl',
MASTER_PASSWORD='wanbin',
MASTER_PORT=3307,
master_auto_position=1;
- 開始主從復制
start slave;
#如果想分別指定啟動線程,可以使用如下命令
START SLAVE IO_THREAD;
START SLAVE SQL_THREAD;
#同樣關閉命令:
STOP SLAVE;
#分別關閉命令:
STOP SLAVE IO_THREAD;
STOP SLAVE SQL_THREAD;
4. 使用gtid進行復制的限制
由於基於GTID的復制依賴於事務,因此在使用時不支持MySQL中可用的某些功能。本節提供有關使用GTID進行復制的限制和限制的信息。
4.1 非事務性存儲引擎的更新
使用GTID時,使用非事務性存儲引擎(如MyISAM)對表的更新不能在與使用事務性存儲引擎(如InnoDB)的表的更新相同的語句或事務中進行。
此限制是由於對使用非事務性存儲引擎的表的更新與對同一事務中使用事務存儲引擎的表的更新混合可能導致將多個GTID分配給同一事務。
當master和slave使用不同的存儲引擎用於同一個表的相應版本時,也會發生這樣的問題,其中一個存儲引擎是事務性的而另一個不是。 還要注意,定義為在非事務性表上運行的觸發器可能是導致這些問題的原因。
在剛剛提到的任何一種情況下,事務和GTID之間的一對一對應關系被破壞,結果是基於GTID的復制無法正常運行。
4.2 CREATE TABLE … SELECT 語句
CREATE TABLE … SELECT對於基於語句的復制是不安全的。 使用基於行的復制時,此語句實際上記錄為兩個單獨的事件 - 一個用於創建表,另一個用於將源表中的行插入剛剛創建的新表中。 當在事務中執行此語句時,在某些情況下,這兩個事件可能會接收相同的事務標識符,這意味着slave將跳過包含插入的事務。 因此,使用基於GTID的復制時不支持CREATE TABLE … SELECT。
4.3 臨時表
使用GTID時(即,enforce_gtid_consistency系統變量設置為ON時),事務,過程,函數和觸發器內不支持CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE語句。 可以在啟用GTID的情況下使用這些語句,但僅限於任何事務之外,並且僅使用autocommit = 1。
4.4 防止執行不受支持的語句
要防止執行會導致基於GTID的復制失敗的語句,必須在啟用GTID時使用–enforce-gtid-consistency選項啟動所有服務器。
4.5 跳過事務
使用GTID時不支持sql_slave_skip_counter
。 如果您需要跳過事務,請使用master的gtid_executed變量的值; 有關詳細信息,請參考
https://dev.mysql.com/doc/refman/5.7/en/replication-gtids-failover.html#replication-gtids-failover-empty
4.6 Ignoring servers
使用GTID時,不推薦使用CHANGE MASTER TO
語句的IGNORE_SERVER_IDS
選項,因為已經應用的事務會自動被忽略。 在啟動基於GTID的復制之前,請檢查並清除之前在相關服務器上設置的所有忽略的服務器ID列表。 可以為各個通道發出的SHOW_SLAVE_STATUS語句顯示已忽略的服務器ID列表(如果有)。 如果沒有列表,則Replicate_Ignore_Server_Ids
字段為空。
4.7 GTID mode and mysqldump
如果目標服務器的二進制日志中沒有GTID,則可以將使用mysqldump創建的轉儲導入到啟用了GTID模式的MySQL服務器中。
4.8 GTID mode and mysql_upgrade
當服務器在啟用全局事務標識符(GTID)的情況下運行時(gtid_mode = ON),請不要通過mysql_upgrade啟用二進制日志記錄(–write-binlog選項)。