全域交易識別碼 (GTID) 是一個獨一無二的識別碼,會在來源伺服器上建立並與每個已提交的交易相關聯。這個識別碼不僅在其來源伺服器上是唯一的,在給定複製拓撲中的所有伺服器上也都是唯一的。
GTID 指派區分了在來源上提交的用戶端交易,以及在複本上重製的複製交易。當用戶端交易在來源上提交時,會被指派一個新的 GTID,前提是該交易已寫入二進位日誌。用戶端交易保證具有單調遞增的 GTID,且產生的數字之間沒有間隙。如果用戶端交易未寫入二進位日誌(例如,因為交易被篩選掉,或交易是唯讀的),則不會在來源伺服器上指派 GTID。
複製交易會保留在來源伺服器上指派給該交易的相同 GTID。GTID 會在複製交易開始執行之前就存在,並且即使複製交易未寫入複本上的二進位日誌,或在複本上被篩選掉,也會持續存在。mysql.gtid_executed
系統資料表用於保留在 MySQL 伺服器上應用之所有交易的已指派 GTID,但儲存在目前作用中二進位日誌檔案中的交易除外。
GTID 的自動跳過功能表示在來源上提交的交易在複本上最多只能應用一次,這有助於保證一致性。一旦具有給定 GTID 的交易在給定伺服器上已提交,任何嘗試執行具有相同 GTID 的後續交易都會被該伺服器忽略。不會引發錯誤,也不會執行交易中的任何陳述式。
如果具有指定 GTID 的交易已在伺服器上開始執行,但尚未提交或回滾,任何嘗試在伺服器上以相同 GTID 開始並行交易的行為都會被封鎖。伺服器既不會開始執行並行交易,也不會將控制權返回給客戶端。一旦首次嘗試交易提交或回滾,先前被相同 GTID 封鎖的並行會話就可以繼續執行。如果首次嘗試回滾,則會有一個並行會話繼續嘗試交易,而其他被相同 GTID 封鎖的並行會話將保持封鎖狀態。如果首次嘗試提交,則所有並行會話都會停止被封鎖,並自動跳過交易的所有語句。
GTID 以一對座標表示,並以冒號字元(:
)分隔,如下所示:
GTID = source_id:transaction_id
source_id
識別起始伺服器。通常,來源的 server_uuid
會用於此目的。transaction_id
是由交易在來源上提交的順序所決定的序號。例如,第一個提交的交易的 transaction_id
為 1
,而在同一個起始伺服器上提交的第十個交易的 transaction_id
則被賦予 10
。交易的 GTID 中不可能將序號設為 0
。例如,最初在 UUID 為 3E11FA47-71CA-11E1-9E33-C80AA9429562
的伺服器上提交的第二十三個交易具有以下 GTID:
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
伺服器實例上的 GTID 序號上限為有符號 64 位整數的非負值數量(263 - 1
或 9223372036854775807
)。如果伺服器用完 GTID,它會採取 binlog_error_action
指定的動作。當伺服器實例接近上限時,會發出警告訊息。
MySQL 9.0 也支援標記的 GTID。一個標記的 GTID 由三個部分組成,並以冒號字元分隔,如下所示:
GTID = source_id:tag:transaction_id
在此情況下,source_id
和 transaction_id
的定義與先前相同。tag
是一個使用者定義的字串,用於識別特定交易群組;請參閱 gtid_next
系統變數的說明,以了解允許的語法。範例:最初在 UUID 為 ed102faf-eb00-11eb-8f20-0c5415bfaa1d
且標籤為 Domain_1
的伺服器上提交的第一百一十七個交易具有以下 GTID:
ed102faf-eb00-11eb-8f20-0c5415bfaa1d:Domain_1:117
交易的 GTID 顯示在 mysqlbinlog 的輸出中,它用於識別效能綱要複寫狀態表格中的個別交易,例如,replication_applier_status_by_worker
。gtid_next
系統變數(@@GLOBAL.gtid_next
)儲存的值是單個 GTID。
GTID 集合是一個由一個或多個單個 GTID 或 GTID 範圍組成的集合。GTID 集合在 MySQL 伺服器中以多種方式使用。例如,gtid_executed
和 gtid_purged
系統變數儲存的值是 GTID 集合。START REPLICA
選項 UNTIL SQL_BEFORE_GTIDS
和 UNTIL SQL_AFTER_GTIDS
可用於讓複本僅處理到 GTID 集合中的第一個 GTID,或在 GTID 集合中的最後一個 GTID 之後停止。內建函數 GTID_SUBSET()
和 GTID_SUBTRACT()
需要 GTID 集合作為輸入。
來自同一伺服器的 GTID 範圍可以摺疊成單一運算式,如下所示:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
上述範例表示來自 MySQL 伺服器的第一到第五個交易,該伺服器的 server_uuid
為 3E11FA47-71CA-11E1-9E33-C80AA9429562
。來自同一伺服器的多個單個 GTID 或 GTID 範圍也可以包含在單一運算式中,其中 GTID 或範圍以冒號分隔,如下列範例所示:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49
GTID 集合可以包含單個 GTID 和 GTID 範圍的任何組合,並且可以包含來自不同伺服器的 GTID。此範例顯示儲存在複本的 gtid_executed
系統變數(@@GLOBAL.gtid_executed
)中的 GTID 集合,該複本已套用來自多個來源的交易:
2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19
當 GTID 集合從伺服器變數返回時,UUID 會依字母順序排列,數值間隔會合併並依遞增順序排列。
在建構 GTID 集合時,使用者定義的標籤會被視為 UUID 的一部分。這表示來自同一伺服器且具有相同標籤的多個 GTID 可以包含在單一運算式中,如此範例所示:
3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:11:47-49
來自同一伺服器但具有不同標籤的 GTID,其處理方式與來自不同伺服器的 GTID 相似,如下所示:
3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:15-21, 3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_2:8-52
GTID 集合的完整語法如下:
gtid_set:
uuid_set [, uuid_set] ...
| ''
uuid_set:
uuid:[tag:]interval[:interval]...
uuid:
hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
h:
[0-9|A-F]
tag:
[a-z_][a-z0-9_]{0,31}
interval:
m[-n]
(m >= 1; n > m)
GTID 儲存在 mysql
資料庫中名為 gtid_executed
的表格中。此表格中的每一列包含它所代表的每個 GTID 或 GTID 集合的起始伺服器 UUID、使用者定義的標籤(如果有的話),以及集合的起始和結束交易 ID;對於僅參考單個 GTID 的列,這最後兩個值相同。
當安裝或升級 MySQL Server 時,會使用類似於此處顯示的 CREATE TABLE
陳述式建立 mysql.gtid_executed
表格(如果該表格尚不存在)。
CREATE TABLE gtid_executed (
source_uuid CHAR(36) NOT NULL,
interval_start BIGINT NOT NULL,
interval_end BIGINT NOT NULL,
gtid_tag CHAR(32) NOT NULL,
PRIMARY KEY (source_uuid, gtid_tag, interval_start)
);
與其他 MySQL 系統表格一樣,請勿嘗試自行建立或修改此表格。
mysql.gtid_executed
表格供 MySQL 伺服器內部使用。它使複本能夠在複本上停用二進位記錄時使用 GTID,並在二進位日誌遺失時保留 GTID 狀態。請注意,如果您發出 RESET BINARY LOGS AND GTIDS
,則會清除 mysql.gtid_executed
表格。
只有當 gtid_mode
為 ON
或 ON_PERMISSIVE
時,才會將 GTID 儲存在 mysql.gtid_executed
表格中。如果停用二進位記錄(log_bin
為 OFF
),或如果停用 log_replica_updates
,則當交易提交時,伺服器會將屬於每個交易的 GTID 與緩衝區中的交易一起儲存,並且背景執行緒會定期將緩衝區的內容以一或多個項目加入到 mysql.gtid_executed
表格中。此外,該表格會以使用者可配置的頻率定期壓縮,如 mysql.gtid_executed 表格壓縮 中所述。
如果啟用二進位記錄(log_bin
為 ON
),則僅針對 InnoDB
儲存引擎,伺服器會以與停用二進位記錄或複本更新記錄時相同的方式更新 mysql.gtid_executed
表格,在交易提交時儲存每個交易的 GTID。對於其他儲存引擎,伺服器僅在二進位日誌輪換或伺服器關閉時才會更新 mysql.gtid_executed
表格。在這些時候,伺服器會將先前二進位日誌中所有交易的 GTID 寫入 mysql.gtid_executed
表格。
如果無法存取 mysql.gtid_executed
表格進行寫入,並且二進位日誌檔案因達到最大檔案大小(max_binlog_size
)以外的任何原因而輪換,則會繼續使用目前的二進位日誌檔案。會將錯誤訊息返回給請求輪換的用戶端,並在伺服器上記錄警告。如果無法存取 mysql.gtid_executed
表格進行寫入,且達到 max_binlog_size
,則伺服器會根據其 binlog_error_action
設定做出回應。如果設定為 IGNORE_ERROR
,則會在伺服器上記錄錯誤,並停止二進位記錄;如果設定為 ABORT_SERVER
,則伺服器會關閉。
隨著時間推移,mysql.gtid_executed
表格可能會填滿許多參考來自同一伺服器、具有相同 GTID 標籤(如果有的話)且其交易 ID 構成範圍的個別 GTID 的列,類似於此處顯示的內容:
+--------------------------------------+----------------+--------------+----------+
| source_uuid | interval_start | interval_end | gtid_tag |
|--------------------------------------+----------------+--------------|----------+
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 31 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 32 | 32 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 33 | 33 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 34 | 34 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 35 | 35 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 36 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 44 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 45 | 45 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 46 | 46 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 47 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 48 | 48 | Domain_1 |
...
為了節省空間,MySQL 伺服器可以透過將每一組列替換為跨越整個交易識別碼間隔的單列,來定期壓縮 mysql.gtid_executed
表格,如下所示:
+--------------------------------------+----------------+--------------+----------+
| source_uuid | interval_start | interval_end | gtid_tag |
|--------------------------------------+----------------+--------------|----------+
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 35 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 39 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 43 | Domain_1 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 46 | Domain_2 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 48 | Domain_1 |
...
伺服器可以使用名為 thread/sql/compress_gtid_table
的專用前景執行緒來執行壓縮。此執行緒不會列在 SHOW PROCESSLIST
的輸出中,但可以在 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
當伺服器啟用二進位日誌記錄時,不會使用此壓縮方法,而是會在每次二進位日誌輪替時壓縮 mysql.gtid_executed
表格。但是,當伺服器停用二進位日誌記錄時,thread/sql/compress_gtid_table
線程會休眠,直到執行指定數量的交易後才會喚醒,然後執行 mysql.gtid_executed
表格的壓縮。接著它會休眠,直到發生相同數量的交易後,再次喚醒執行壓縮,並無限重複此循環。表格壓縮前的交易次數,以及壓縮率,由 gtid_executed_compression_period
系統變數的值控制。將該值設定為 0 表示該線程永遠不會喚醒,這表示不會使用此明確的壓縮方法。相反地,壓縮會在需要時隱式發生。
與 InnoDB
以外的儲存引擎相關的交易使用不同的流程,InnoDB
交易會由一個獨立的流程寫入 mysql.gtid_executed
表格。這個流程由不同的線程 innodb/clone_gtid_thread
控制。此 GTID 持久化線程會將 GTID 分組收集,將它們刷新到 mysql.gtid_executed
表格,然後壓縮表格。如果伺服器同時有 InnoDB
交易和非 InnoDB
交易(這些交易會個別寫入 mysql.gtid_executed
表格),則 compress_gtid_table
線程執行的壓縮會干擾 GTID 持久化線程的工作,並可能顯著降低其速度。因此,建議您將 gtid_executed_compression_period
設定為 0,以便永遠不啟用 compress_gtid_table
線程。
gtid_executed_compression_period
的預設值為 0,所有交易(無論儲存引擎為何)都會由 GTID 持久化線程寫入 mysql.gtid_executed
表格。
當伺服器實例啟動時,如果 gtid_executed_compression_period
設定為非零值,且啟動 thread/sql/compress_gtid_table
線程,則在大多數伺服器配置中,會對 mysql.gtid_executed
表格執行明確的壓縮。壓縮會由線程啟動觸發。