實驗環境
此次實驗的環境如下
- MySQL 5.7.25
- Redhat 6.10
- 操作系統賬號:mysql
- 數據庫復制賬號:repl
- 復制格式:基于行的復制

通過前面的介紹我們知道MySQL的復制有兩種方法
- 基于二進制日志文件位置
- 基于GTID
上一節的內容為GTID的格式和存儲,這節根據官方文檔我們說GTID的生命周期
1. GTID生命周期
這里以一個事務從主庫執行到從庫應用的過程來講解
1.1 主庫提交事務被分配GTID
當主庫執行和提交一個事務后,該事務會被分配一個GTID(主庫uuid和最小的未被使用過的事務號),之后會被寫入到二進制日志文件中,其位置在具體事務之前
如果一個事務沒有被寫入二進制文件,例如被過濾掉或者是只讀的,則不會被分配GTID
1.2 主庫GTID寫入二進制文件
分配GTID后,該GTID會在提交的時候以Gtid_log_event 事件的形式寫入二進制日志文件中,其位置在具體事務之前
當日志發生切換或者數據庫關閉時該GTID會被寫入到mysql.gtid_executed表中
我們可以通過mysqlbinlog命令看出

1.3 主庫寫入到GLOBAL.gtid_executed系統變量中
在提交時,還會將該GTID加入到GLOBAL.gtid_executed系統變量中
該變量是個GTID集合,代表目前為止所有被執行過的事務,主要用于復制中
我們也可以同如下命令查看
show master status;

1.4 從庫接收主庫GTID事務
當二進制日志被傳輸到備庫后,會被儲存在relay 日志中,從庫會讀取該GTID并設置
gtid_next變量為該GTID
這樣就告訴備庫下一個執行的事務必須為該GTID
需要注意的是該變量是session級別的
1.5 從庫應用主庫GTID事務
在接收到主庫GTID事務并設置好gtid_next后,如果沒有其他進程在執行的話,從庫執行該GTID事務
如果同時有多個進程執行該事務,則會選擇其中一個(如多線程復制)
我們可以查詢gtid_owned系統變量來確認
select @@GLOBAL.gtid_owned

由于該事務已經被主庫分配了GTID,所以從庫上的該事務不會被分配GTID,而是使用gtid_next變量的值
1.6 從庫寫入二進制文件日志
由于從庫重新執行了來自主庫的事務,所以他也會寫日志到從庫的二進制日志文件中
這里分兩種情況
- 如果從庫啟用了二進制日志功能,則和1.2步驟一致
- 如果從庫未啟用二進制日志功能,則會將gtid和對應的事務語句寫入到mysql.gtid_executed表中
需要注意的是如果未開啟二進制日志功能MySQL 5.7及之前只有DML操作是原子級別的,DDL并不是,意味著如果MySQL發生異常,數據可能會變得不一致
MySQL 8.0后所有操作都支持
1.7 從庫寫入到GLOBAL.gtid_executed系統變量中
同樣的在提交后,從庫也會將該GTID寫入到從庫的gtid_executed系統變量中
2. 多線程復制
如果啟用了多線程復制(slave_parallel_workers > 0),由于是并行執行的GTID事務,每個線程負責不同的GTID,這時gtid_executed變量的值可能會有GAP,系統會自動更新這些值
3. 什么動作會被分配GTID
所有的數據庫更改(DML或DDL)都會被分配GTID
數據庫的新增刪除修改也會被分配GTID
非事務型的修改也會被記錄下來,如果在寫入過程中發生異常,則會記錄一個incident事件
如下操作可能會讓一個事務分配多個GTID
- 一個存儲過程包含多個事務
- drop table語句一次包含多個表
- CREATE TABLE … SELECT 語句,他會被分配一個create table GTID和一個insert GTID
7. 參考資料
本專題內容翻譯自官方文檔并結合自己的環境
16.1.3.2 GTID Life Cycle?dev.mysql.com