MySQL數據庫主從技術GTID大揭秘
一、概述
1.1 GTID的概念
GTID(全局事務標識符)是mysql MySQL-5.6.5開始支持的新特性之一,全局事務標識符不僅在源(主)服務器上是唯一的,而且在給定復制設置中的所有服務器都是唯一的。正因為這樣一個特性使得mysql的主從復制變得更加簡單且一致性更高。它與源(主)服務器上提交的每個事務相關聯,由服務器ID+事務ID組合而成。
GTID = source_id:transaction_id,中間由“:”分隔,source_id用于標識原服務器,通常指server_uuid,由于GTID會傳遞到slave,所以也可以理解為源ID。transaction_id為當前服務器上已提交事務的一個序列號,通常從1開始自增長的序列,一個數值對應一個事務。
查看本機的server_uuid方法如下:
- mysql> show variables like '%uuid%';
- +---------------+--------------------------------------+
- | Variable_name | Value |
- +---------------+--------------------------------------+
- | server_uuid | f3d0a8b5-a657-11eb-a6e5-000c29dbd935 |
- +---------------+--------------------------------------+
- 1 row in set (0.01 sec)
1.2 GTID的工作原理
1、當一個事務在主庫端執行并提交時,會產生一個GTID并記錄到binlog日志中。
2、binlog傳輸到slave,并存儲到slave的relaylog后,讀取這個GTID的這個值設置gtid_next變量,即告訴Slave,下一個要執行的GTID值。
3、sql線程從relay log中獲取GTID,然后對比slave端的binlog是否有該GTID,如果有說明該GTID的事務已經執行,slave會忽略。如果沒有記錄,slave會執行該GTID的事務,在執行前會檢查其他session持有該GTID,確保該GTID的事務不會被重復執行,并記錄該GTID到自身的binlog。
4、在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,再沒有就走全表掃描。
1.3 mysql.gtid_exected表
GTID存儲在mysql的 gtid_executed的表中。該表供mysql服務器內部使用,該表中的一行代表的每個GTID或GTID集合,以及該集合的開始和結束事務ID;對于僅引用單個GTID的行,最后兩個值相同。
在mysql.gtid_executed安裝或升級MySQL Server時,使用create table 類似于以下所示的語句創建該表(如果尚不存在):
- CREATE TABLE gtid_executed (
- source_uuid CHAR(36) NOT NULL,
- interval_start BIGINT(20) NOT NULL,
- interval_end BIGINT(20) NOT NULL,
- PRIMARY KEY (source_uuid, interval_start)
- )
注意:與其他MySQL系統表一樣,請勿嘗試自己創建或修改該表。
GTID僅當gtid_mode is ON or ON_PERMISSIVE時,GTID才會存儲在gtid_executed的表中,GTID的存儲與mysql是否啟用二進制日志緊密相關。
- 如果禁用了二進制日志記錄(log_binis OFF),或者如果 log_slave_updates禁用了二進制日志記錄,則服務器將屬于每個事務的GTID與該事務一起存儲在表中。此外,該表會以用戶可配置的速率定期壓縮。這種情況僅適用于禁用了二進制日志記錄或副本更新日志記錄的副本。它不適用于復制源服務器,因為必須在源上啟用二進制日志記錄才能進行復制。
- 如果啟用了二進制日志記錄(log_bin是 ON),則每當旋轉二進制日志或關閉服務器時,服務器都會將寫入前一個二進制日志的所有事務的GTID寫入mysql.gtid_executed表中。這種情況適用于復制源服務器或啟用了二進制日志記錄的副本。
- 如果服務器意外停止,則當前二進制日志文件中的GTID集不會保存在 mysql.gtid_executed表中。恢復期間,這些GTID從二進制日志文件添加到表中。例外的是重新啟動服務器時未啟用二進制日志記錄。在這種情況下,服務器無法訪問二進制日志文件以恢復GTID,因此無法啟動復制。
啟用二進制日志記錄后,該 mysql.gtid_executed表將不保存所有已執行事務的GTID的完整記錄。該信息由gtid_executed系統變量的全局值提供 。始終使用 @@GLOBAL.gtid_executed,它在每次提交后都會更新,以表示MySQL服務器的GTID狀態,而不查詢 mysql.gtid_executed表。
1.4 mysql.gtid_executed表壓縮
隨著時間的流逝, mysql.gtid_executed表中可能會出現很多行,表會越來越大,為了節省空間,MySQL服務器mysql.gtid_executed通過用橫跨事務標識符整個間隔的一行替換每行這樣的行來定期壓縮 表,如下所示:
- +--------------------------------------+----------------+--------------+
- | source_uuid | interval_start | interval_end |
- |--------------------------------------+----------------+--------------|
- | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 43 |
- ...
您可以通過設置
gtid_executed_compression_period 系統變量來控制壓縮速率,此變量的默認值為1000,這意味著默認情況下,每1000個事務處理后將對表進行壓縮。設置 gtid_executed_compression_period 為0根本無法執行壓縮,gtid_executed如果您這樣做,應該準備增加表可能需要的磁盤空間量 。
該mysql.gtid_executed表的壓縮由名為的專用前臺線程執行
thread/sql/compress_gtid_table。該線程未在的輸出中列出SHOW PROCESSLIST,但可以在threads表中的一行中查看 ,如下所示:
- mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
- RESOURCE_GROUP: SYS_default
- *************************** 2. row ***************************
- THREAD_ID: 45
- NAME: thread/sql/compress_gtid_table
- TYPE: FOREGROUND
- PROCESSLIST_ID: 6
- PROCESSLIST_USER: NULL
- PROCESSLIST_HOST: NULL
- PROCESSLIST_DB: NULL
- PROCESSLIST_COMMAND: Daemon
- PROCESSLIST_TIME: 8757
- PROCESSLIST_STATE: Suspending
- PROCESSLIST_INFO: NULL
- PARENT_THREAD_ID: 1
- ROLE: NULL
- INSTRUMENTED: YES
- HISTORY: YES
- CONNECTION_TYPE: NULL
- THREAD_OS_ID: 7602
- RESOURCE_GROUP: SYS_default
- 2 rows in set (0.01 sec)
二、GTID復制的優缺點
2.1 GTID優勢
1、更簡單的實現failover,不用以前那樣在需要找log_file和log_pos。 2、更簡單的搭建主從復制。 3、比傳統的復制更加安全。 4、GTID是連續的沒有空洞的,保證數據的一致性,零丟失。
2.2 GTID的限制
1、不允許在同一個事務內對事務表和非事務進行DML操作,例如在同一個事務內先update innodb表,然后update myisam表。因為GTID強制每一個GTID對應一個事務,而在同一個事務內既操作innodb表又操作myisam,就會產生兩個GTID; 2、不允許CREATE TABLE … SELECT語句,首先這種語句對于statement格式的binlog是不安全的;而對于row格式的binlog,這種語句在binlog實際是分成兩個event進行記錄的,一個記錄create創建操作,一個記錄insert操作,那么就有可能這兩個操作是對應到同一個GTID上,而當將這兩個擁有相同GTID的event傳到從庫時,從庫就會忽略擁有相同GTID的insert操作,造成數據丟失; 3、CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE不允許在事務內執行,只有在事務以外并且autocommit=1才能正常執行; 4、不支持sql_slave_skip_counter,如果需要跳過事務,可以用以下方法:
- set @@session.gtid_next='需要跳過的事務gtid'
- begin;commit;
- set session gtid_next=automatic;
【編輯推薦】