GTID 的生命週期包含下列步驟
在來源上執行並提交交易。此用戶端交易會被指派一個 GTID,其組成包括來源的 UUID 和該伺服器上尚未使用的最小非零交易序列號。GTID 會寫入來源的二進制日誌 (在日誌中緊接於交易本身之前)。如果用戶端交易未寫入二進制日誌 (例如,因為交易被篩選掉,或交易是唯讀的),則不會指派 GTID。
如果為交易指派了 GTID,則 GTID 會在提交時以原子方式持久保存,方法是將其寫入交易開頭的二進制日誌 (作為
Gtid_log_event
)。每當二進制日誌旋轉或伺服器關閉時,伺服器會將所有寫入先前二進制日誌檔案的交易的 GTID 寫入mysql.gtid_executed
表格。如果交易被指派了 GTID,則 GTID 會以非原子性的方式外部化(在交易提交後很短的時間內),方法是將其添加到
gtid_executed
系統變數(@@GLOBAL.gtid_executed
)中的 GTID 集合中。這個 GTID 集合包含所有已提交 GTID 交易的表示,並且在複製中用作代表伺服器狀態的標記。當啟用二進位日誌記錄(來源伺服器的必要條件)時,gtid_executed
系統變數中的 GTID 集合是應用交易的完整記錄,但mysql.gtid_executed
表格則不是,因為最新的歷史記錄仍在目前的二進位日誌檔中。在二進位日誌資料傳輸到複本並儲存在複本的中繼日誌中(使用此過程的既有機制,詳情請參閱 第 19.2 節「複製實作」)之後,複本會讀取 GTID,並將其
gtid_next
系統變數的值設定為此 GTID。這會告知複本,下一個交易必須使用此 GTID 記錄。請務必注意,複本會在會話上下文中設定gtid_next
。複本會驗證沒有任何執行緒已經取得
gtid_next
中的 GTID 的所有權,以便處理交易。透過在處理交易本身之前,先讀取並檢查複製交易的 GTID,複本不僅保證先前沒有具有此 GTID 的交易已應用於複本上,還保證沒有其他會話已經讀取此 GTID,但尚未提交相關的交易。因此,如果多個客戶端嘗試同時應用相同的交易,伺服器會透過讓其中一個客戶端執行來解決此問題。複本的gtid_owned
系統變數(@@GLOBAL.gtid_owned
)會顯示目前正在使用中的每個 GTID 以及擁有它的執行緒 ID。如果 GTID 已被使用,則不會引發錯誤,並且會使用自動跳過功能來忽略該交易。如果 GTID 尚未使用,複本會應用複製的交易。由於
gtid_next
設定為來源伺服器已經指派的 GTID,因此複本不會嘗試為此交易產生新的 GTID,而是改為使用儲存在gtid_next
中的 GTID。如果複本上啟用了二進位日誌記錄,則 GTID 會在提交時以原子方式保存,方法是在交易開始時將其寫入二進位日誌(作為
Gtid_log_event
)。每當二進位日誌旋轉或伺服器關閉時,伺服器會將寫入先前二進位日誌檔中的所有交易的 GTID 寫入mysql.gtid_executed
表格中。如果複本上停用了二進位日誌記錄,則 GTID 會以原子方式保存,方法是將其直接寫入
mysql.gtid_executed
表格中。MySQL 會將一個語句附加到交易中,以將 GTID 插入到表格中。此操作對於 DDL 語句以及 DML 語句都是原子性的。在這種情況下,mysql.gtid_executed
表格是應用於複本的交易的完整記錄。在複本上提交複製的交易後很短的時間內,GTID 會以非原子方式外部化,方法是將其添加到複本的
gtid_executed
系統變數(@@GLOBAL.gtid_executed
)中的 GTID 集合中。與來源伺服器一樣,這個 GTID 集合包含所有已提交 GTID 交易的表示。如果複本上停用了二進位日誌記錄,則mysql.gtid_executed
表格也是應用於複本的交易的完整記錄。如果複本上啟用了二進位日誌記錄,表示某些 GTID 僅記錄在二進位日誌中,則gtid_executed
系統變數中的 GTID 集合是唯一完整的記錄。
在來源伺服器上完全篩選掉的客戶端交易不會被指派 GTID,因此它們不會被添加到 gtid_executed
系統變數中的交易集合中,也不會被添加到 mysql.gtid_executed
表格中。但是,在複本上完全篩選掉的複製交易的 GTID 會被保存。如果複本上啟用了二進位日誌記錄,則篩選掉的交易會以 Gtid_log_event
的形式寫入二進位日誌,然後是一個僅包含 BEGIN
和 COMMIT
語句的空交易。如果停用了二進位日誌記錄,則會將篩選掉的交易的 GTID 寫入 mysql.gtid_executed
表格中。保留篩選掉的交易的 GTID 可確保 mysql.gtid_executed
表格和 gtid_executed
系統變數中的 GTID 集合可以被壓縮。它還可以確保,如果複本重新連線到來源伺服器,則不會再次檢索篩選掉的交易,如 第 19.1.3.3 節「GTID 自動定位」中所述。
在多執行緒複本上(使用 replica_parallel_workers > 0
),可以並行應用交易,因此複製的交易可能會以無序的方式提交(除非 replica_preserve_commit_order = 1
)。發生這種情況時,gtid_executed
系統變數中的 GTID 集合包含多個 GTID 範圍,它們之間存在間隙。(在來源伺服器或單執行緒複本上,存在單調遞增的 GTID,數字之間沒有間隙。)多執行緒複本上的間隙僅發生在最近應用的交易中,並且隨著複製的進展而填補。當使用 STOP REPLICA
語句乾淨地停止複製執行緒時,會應用正在進行的交易,以便填補間隙。在發生諸如伺服器故障或使用 KILL
語句來停止複製執行緒之類的關閉事件時,間隙可能會保留。
典型情況是伺服器為已提交的交易產生新的 GTID。但是,GTID 也可以指派給交易之外的其他變更,並且在某些情況下,單個交易可以被指派多個 GTID。
寫入二進位日誌的每個資料庫變更(DDL 或 DML)都會被指派一個 GTID。這包括自動提交的變更,以及使用 BEGIN
和 COMMIT
或 START TRANSACTION
語句提交的變更。GTID 也會被指派給資料庫以及非表格資料庫物件(例如程序、函數、觸發器、事件、視圖、使用者、角色或授權)的建立、變更或刪除。
非交易更新以及交易更新都會被指派 GTID。此外,對於非交易更新,如果在嘗試寫入二進位日誌快取時發生磁碟寫入失敗,並且因此在二進位日誌中產生間隙,則產生的事件日誌事件會被指派一個 GTID。
當表格由二進位日誌中產生的語句自動刪除時,該語句會被指派一個 GTID。當複本開始應用來自剛啟動的來源伺服器的事件時,會自動刪除暫時表格,並且當使用基於語句的複製時(binlog_format=STATEMENT
)且具有打開的暫時表格的使用者會話斷開連線時,也會自動刪除暫時表格。使用 MEMORY
儲存引擎的表格會在伺服器啟動後第一次存取時自動刪除,因為在關閉期間可能會遺失資料列。
當交易沒有寫入來源伺服器的二進位日誌時,伺服器不會為其指派 GTID。這包括回滾的交易,以及在來源伺服器上停用二進位日誌記錄時執行的交易(無論是全域停用(伺服器的組態中指定了 --skip-log-bin
)還是為會話停用(SET @@SESSION.sql_log_bin = 0
))。這還包括在使用基於資料列的複製時(binlog_format=ROW
)的空操作交易。
XA 交易會為交易的 XA PREPARE
階段以及交易的 XA COMMIT
或 XA ROLLBACK
階段指派單獨的 GTID。XA 交易會被持久性準備,以便使用者可以在發生故障時(在複製拓樸中可能包括容錯移轉到另一台伺服器)提交或回滾它們。因此,交易的兩個部分會被單獨複製,因此它們必須具有自己的 GTID,即使回滾的非 XA 交易不會有 GTID。
在以下特殊情況下,單個語句可以產生多個交易,因此可以被指派多個 GTID
調用了會提交多個交易的儲存程序。為程序提交的每個交易產生一個 GTID。
多表格
DROP TABLE
語句會刪除不同類型的表格。如果任何表格使用不支援原子 DDL 的儲存引擎,或者如果任何表格是暫時表格,則可以產生多個 GTID。當使用基於列的複製時(
binlog_format=ROW
),會發出CREATE TABLE ... SELECT
陳述式。針對CREATE TABLE
動作會產生一個 GTID,而針對列插入動作會產生一個 GTID。
預設情況下,對於使用者會話中提交的新交易,伺服器會自動產生並指派新的 GTID。當交易應用於副本時,會保留來自原始伺服器的 GTID。您可以透過設定 gtid_next
系統變數的會話值來變更此行為。
當
gtid_next
設定為AUTOMATIC
(預設值)且交易已提交並寫入二進制日誌時,伺服器會自動產生並指派新的 GTID。如果交易回滾或因其他原因未寫入二進制日誌,則伺服器不會產生並指派 GTID。如果您將
gtid_next
設定為AUTOMATIC:
,則會為每個新交易指派一個包含指定標籤的新 GTID。TAG
如果您將
gtid_next
設定為有效的 GTID(由 UUID、可選標籤和交易序號組成,以冒號分隔),則伺服器會將該 GTID 指派給您的交易。即使交易未寫入二進制日誌,或者交易為空時,也會指派此 GTID 並將其新增至gtid_executed
。
請注意,在您將 gtid_next
設定為特定 GTID(格式為
或 UUID
:NUMBER
)後,且該交易已提交或回滾,則必須在任何其他陳述式之前發出明確的 UUID
:TAG
:NUMBER
SET @@SESSION.gtid_next
陳述式。如果您不想再明確指派任何 GTID,您可以使用此陳述式將 GTID 值設定回 AUTOMATIC
。
當複製應用程式執行緒應用複製的交易時,它們會使用此技術,將 @@SESSION.gtid_next
明確設定為複製交易在原始伺服器上指派的 GTID。這表示會保留來自原始伺服器的 GTID,而不是由副本產生並指派新的 GTID。這也表示即使在副本上停用二進制日誌記錄或副本更新日誌記錄,或者交易為空操作或在副本上被篩選掉時,也會將 GTID 新增至副本上的 gtid_executed
。
用戶端可以透過在執行交易之前將 @@SESSION.gtid_next
設定為特定的 GTID 來模擬複製的交易。 mysqlbinlog 使用此技術來產生二進制日誌的傾印,用戶端可以重播該傾印以保留 GTID。透過用戶端提交的模擬複製交易與透過複製應用程式執行緒提交的複製交易完全相同,事後無法區分。
gtid_purged
系統變數 (@@GLOBAL.gtid_purged
) 中的 GTID 集包含已在伺服器上提交的所有交易的 GTID,但這些 GTID 不存在於伺服器上的任何二進制日誌檔中。gtid_purged
是 gtid_executed
的子集。gtid_purged
中包含以下類別的 GTID:
在副本上停用二進制日誌記錄的情況下提交的複製交易的 GTID。
已寫入現已清除的二進制日誌檔中的交易的 GTID。
透過陳述式
SET @@GLOBAL.gtid_purged
明確新增至集合中的 GTID。
您可以變更 gtid_purged
的值,以便在伺服器上記錄已應用某個 GTID 集中交易的情況,即使這些交易不存在於伺服器上的任何二進制日誌中。當您將 GTID 新增至 gtid_purged
時,它們也會新增至 gtid_executed
。此動作的一個範例使用案例是,當您在伺服器上還原一個或多個資料庫的備份時,但您沒有包含伺服器上交易的相關二進制日誌。您也可以選擇是否將 gtid_purged
中的整個 GTID 集取代為指定的 GTID 集,或是將指定的 GTID 集新增至 gtid_purged
中已有的 GTID。有關如何執行此操作的詳細資訊,請參閱 gtid_purged
的說明。
gtid_executed
和 gtid_purged
系統變數中的 GTID 集會在伺服器啟動時初始化。每個二進制日誌檔都以事件 Previous_gtids_log_event
開頭,其中包含所有先前二進制日誌檔中的 GTID 集(由前一個檔案的 Previous_gtids_log_event
中的 GTID 和前一個檔案本身中每個 Gtid_log_event
的 GTID 組成)。最舊和最新二進制日誌檔中 Previous_gtids_log_event
的內容用於計算伺服器啟動時的 gtid_executed
和 gtid_purged
集。
gtid_executed
的計算方式為:最新二進制日誌檔中Previous_gtids_log_event
中的 GTID、該二進制日誌檔中交易的 GTID 以及儲存在mysql.gtid_executed
表中的 GTID 的聯集。此 GTID 集包含伺服器上已使用(或明確新增至gtid_purged
)的所有 GTID,無論它們目前是否位於伺服器上的二進制日誌檔中。它不包含伺服器上目前正在處理的交易的 GTID(@@GLOBAL.gtid_owned
)。gtid_purged
的計算方式為:首先加入最新二進制日誌檔中Previous_gtids_log_event
中的 GTID 和該二進制日誌檔中交易的 GTID。此步驟會提供目前或曾經記錄在伺服器上二進制日誌中的 GTID 集 (gtids_in_binlog
)。接下來,從gtids_in_binlog
中減去最舊二進制日誌檔中Previous_gtids_log_event
中的 GTID。此步驟會提供目前記錄在伺服器上二進制日誌中的 GTID 集 (gtids_in_binlog_not_purged
)。最後,從gtid_executed
中減去gtids_in_binlog_not_purged
。結果是已在伺服器上使用但目前未記錄在伺服器上二進制日誌檔中的 GTID 集,此結果用於初始化gtid_purged
。
如果您需要在伺服器上重設 GTID 執行歷程記錄,請使用 RESET BINARY LOGS AND GTIDS
陳述式。在新的啟用 GTID 的伺服器上執行測試查詢以驗證複寫設定之後,或者當您想要將新伺服器加入複寫群組但它包含某些群組複寫不接受的不想要本機交易時,您可能需要執行此操作。
請謹慎使用 RESET BINARY LOGS AND GTIDS
,以避免遺失任何想要的 GTID 執行歷程記錄和二進制日誌檔。
在發出 RESET BINARY LOGS AND GTIDS
之前,請確保您已備份伺服器的二進制日誌檔和二進制日誌索引檔(如果有的話),並取得並儲存 gtid_executed
系統變數的全域值中保存的 GTID 集(例如,透過發出 SELECT @@GLOBAL.gtid_executed
陳述式並儲存結果)。如果您要從該 GTID 集中移除不想要的交易,請使用 mysqlbinlog 來檢查交易的內容,以確保它們沒有任何值,不包含必須儲存或複製的資料,並且不會在伺服器上導致資料變更。
當您發出 RESET BINARY LOGS AND GTIDS
時,會執行以下重設操作:
gtid_purged
系統變數的值會設定為空字串 (''
)。gtid_executed
系統變數的全域值(但不是會話值)會設定為空字串。會清除
mysql.gtid_executed
表(請參閱 mysql.gtid_executed 表)。如果伺服器已啟用二進制日誌記錄,則現有的二進制日誌檔案會被刪除,且二進制日誌索引檔案會被清除。
請注意,即使伺服器是已停用二進制日誌記錄的副本,RESET BINARY LOGS AND GTIDS
語句是用於重設 GTID 執行歷史記錄的方法。RESET REPLICA
語句則對 GTID 執行歷史記錄沒有影響。