来自实战45讲
GTID 的全称是 Global Transaction Identifier,也就是全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。它由两部分组成,格式是:
GTID=server_uuid:gno
其中:
server_uuid 是一个实例第一次启动时自动生成的,是一个全局唯一的值;
gno 是一个整数,初始值是 1,每次提交事务的时候分配给这个事务,并加 1。
这里我需要和你说明一下,在 MySQL 的官方文档里,GTID 格式是这么定义的:
GTID=source_id:transaction_id
这里的 source_id 就是 server_uuid;而后面的这个 transaction_id,我觉得容易造成误导,所以我改成了 gno。为什么说使用 transaction_id 容易造成误解呢?
因为,
在 MySQL 里面我们说 transaction_id 就是指事务 id,事务 id 是在事务执行过程中分配的,如果这个事务回滚了,事务 id 也会递增,而 gno 是在事务提交的时候才会分配。
从效果上看,GTID 往往是连续的,因此我们用 gno 来表示更容易理解。
GTID 模式的启动也很简单,我们只需要在启动一个 MySQL 实例的时候,加上参数 gtid_mode=on 和 enforce_gtid_consistency=on 就可以了。
在 GTID 模式下,每个事务都会跟一个 GTID 一一对应。这个 GTID 有两种生成方式,而使用哪种方式取决于 session 变量 gtid_next 的值。
如果 gtid_next=automatic,代表使用默认值。这时,MySQL 就会把 server_uuid:gno 分配给这个事务。
a. 记录 binlog 的时候,先记录一行 SET @@SESSION.GTID_NEXT=‘server_uuid:gno’;
b. 把这个 GTID 加入本实例的 GTID 集合。
如果 gtid_next 是一个指定的 GTID 的值,比如通过 set gtid_next='current_gtid’指定为 current_gtid,那么就有两种可能:
a. 如果 current_gtid 已经存在于实例的 GTID 集合中,接下来执行的这个事务会直接被系统忽略;
b. 如果 current_gtid 没有存在于实例的 GTID 集合中,就将这个 current_gtid 分配给接下来要执行的事务,也就是说系统不需要给这个事务生成新的 GTID,因此 gno 也不用加 1。
注意,一个 current_gtid 只能给一个事务使用。这个事务提交后,如果要执行下一个事务,就要执行 set 命令,把 gtid_next 设置成另外一个 gtid 或者 automatic。
这样,每个 MySQL 实例都维护了一个 GTID 集合,用来对应“这个实例执行过的所有事务”。
实验1:证明事务id是在事务开始的时候就分配了,事务回滚后,事务id也会加1
session 1模拟一个没有提交的事务:
root@localhost : liuwenhe 21:38:19>select * from liuwenhe;
+----+------+-------+
| id | name | name2 |
+----+------+-------+
| 1 | 1 | 1 |
+----+------+-------+
1 row in set (0.00 sec)
root@localhost : liuwenhe 21:38:27>begin;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 21:38:33>delete from liuwenhe where id=1;
Query OK, 1 row affected (0.01 sec)
session2查看session1的事务id:
root@localhost : information_schema 21:43:28>select trx_id from INNODB_TRX;
+---------+
| trx_id |
+---------+
| 1026178 |
+---------+
1 row in set (0.00 sec)
然后session1事务回滚:
root@localhost : liuwenhe 21:41:10>rollback;
Query OK, 0 rows affected (0.00 sec)
然后再次执行一个事务,不提交
root@localhost : liuwenhe 21:45:50>begin;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 21:47:10>delete from liuwenhe where id=1;
Query OK, 1 row affected (0.00 sec)
session2查看:发现事务号依旧是增大的,发现增大了5!
root@localhost : information_schema 21:47:13>select trx_id from INNODB_TRX;
+---------+
| trx_id |
+---------+
| 1026183 |
+---------+
1 row in set (0.00 sec)
实验2:当一个事务A正常commit的时候,下一个事务B的事务A的事务号加1;当一个事务A rollback的时候,那么下一个事务B的事务号是A的事务号加5!
2.1正常提交后下一个事务是加1
root@localhost : liuwenhe 21:56:12>begin;
Query OK, 0 rows affected (0.01 sec)
root@localhost : liuwenhe 21:56:17>insert into liuwenhe values(2556,2,3);
Query OK, 1 row affected (0.00 sec)
root@localhost : liuwenhe 21:56:22>SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID(); #查看当前连接的事务id!
+---------+
| TRX_ID |
+---------+
| 1026220 |
+---------+
1 row in set (0.00 sec)
root@localhost : liuwenhe 21:56:26>commit;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 21:56:35>begin;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 21:56:38>insert into liuwenhe values(25556,2,3);
Query OK, 1 row affected (0.00 sec)
root@localhost : liuwenhe 21:56:43>SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
+---------+
| TRX_ID |
+---------+
| 1026221 |
+---------+
1 row in set (0.00 sec)
2.2回滚后事务号加5
root@localhost : liuwenhe 21:56:46>rollback;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 21:56:51>begin;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 21:56:54>insert into liuwenhe values(425556,2,3);
Query OK, 1 row affected (0.01 sec)
root@localhost : liuwenhe 21:56:58>SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
+---------+
| TRX_ID |
+---------+
| 1026226 |
+---------+
实验3 GTID只有提交后的事务,对应的DTID才会加1,所以gtid是连续的,事务id可能不是连续的!
首先正常提交加1:
root@localhost : liuwenhe 22:14:38>show master status\G
*************************** 1. row ***************************
File: mysql-bin.000019
Position: 6395
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 35b7c32f-57e3-11eb-a2c8-005056a3b3a1:1-5,
c4826a01-a81c-ee14-5584-ed921dde35bf:1-
922422
1 row in set (0.00 sec)
root@localhost : liuwenhe 22:15:09>insert into liuwenhe values(36,2,3);
Query OK, 1 row affected (0.00 sec)
root@localhost : liuwenhe 22:15:14>show master status\G
*************************** 1. row ***************************
File: mysql-bin.000019
Position: 6672
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 35b7c32f-57e3-11eb-a2c8-005056a3b3a1:1-5,
c4826a01-a81c-ee14-5584-ed921dde35bf:1-
922423
1 row in set (0.00 sec)
如果事务rollback,发现gtid不变
root@localhost : liuwenhe 22:26:28>show master status\G
*************************** 1. row ***************************
File: mysql-bin.000019
Position: 7399
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 35b7c32f-57e3-11eb-a2c8-005056a3b3a1:1-5,
c4826a01-a81c-ee14-5584-ed921dde35bf:1-922426
1 row in set (0.00 sec)
root@localhost : liuwenhe 22:30:32>begin;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 22:30:36>insert into liuwenhe values(3326,2,3);
Query OK, 1 row affected (0.00 sec)
root@localhost : liuwenhe 22:30:44>rollback;
Query OK, 0 rows affected (0.00 sec)
root@localhost : liuwenhe 22:32:13>show master status\G
*************************** 1. row ***************************
File: mysql-bin.000019
Position: 7399
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 35b7c32f-57e3-11eb-a2c8-005056a3b3a1:1-5,
c4826a01-a81c-ee14-5584-ed921dde35bf:1-922426
1 row in set (0.00 sec)
综上所述:如果事务正常提交,发现gtid后半部分是加1,并且下一个事务的id也是加1,但是如果某个事务rollback,那么gtid不变,并且下一个事务的事务id加5, 并且gtid的后半部分不是事务id!GTID是在事务提交的时候加1,所以gtid是连续的,但是事务id是在事务开始运行的时候就给了,并且不管事务提交与否都增大!