全域交易識別碼 (GTID) 是一個獨特的識別碼,會建立並與來源 (source) 伺服器上提交的每筆交易產生關聯。此識別碼不僅對於其來源伺服器是獨一無二的,而且在給定的複製拓撲中的所有伺服器中都是獨一無二的。
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 8.4 也支援帶標籤的 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 儲存在名為 gtid_executed
的表格中,位於 mysql
資料庫中。此表格中的每一列都包含它所代表的每個 GTID 或 GTID 集合的來源伺服器 UUID、使用者定義的標籤(如果有的話)以及集合的起始和結束交易 ID;對於僅參考單個 GTID 的列,最後兩個值是相同的。
當安裝或升級 MySQL 伺服器時,會使用類似於此處所示的 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
表格執行顯式壓縮。壓縮會由執行緒啟動觸發。