此次实验的环境如下
MySQL 5.7.25
Redhat 6.10
操作系统账号:mysql
数据库复制账号:repl
复制格式:基于行的复制
IP地址 | 主从关系 | 复制账号 | 复制格式 |
---|---|---|---|
11.12.14.29 | 主库 | repl | Row-Based |
11.12.14.30 | 从库(半同步) | repl | Row-Based |
11.12.14.31 | 从库(异步) | repl | Row-Based |
通过前面的介绍我们知道MySQL的复制有两种方法
上一节的内容为GTID的格式和存储,这节根据官方文档我们说GTID的生命周期
这里以一个事务从主库执行到从库应用的过程来讲解
当主库执行和提交一个事务后,该事务会被分配一个GTID(主库uuid和最小的未被使用过的事务号),之后会被写入到二进制日志文件中,其位置在具体事务之前
如果一个事务没有被写入二进制文件,例如被过滤掉或者是只读的,则不会被分配GTID
分配GTID后,该GTID会在提交的时候以Gtid_log_event 事件的形式写入二进制日志文件中,其位置在具体事务之前
当日志发生切换或者数据库关闭时该GTID会被写入到mysql.gtid_executed表中
我们可以通过mysqlbinlog命令看出
在提交时,还会将该GTID加入到GLOBAL.gtid_executed系统变量中
该变量是个GTID集合,代表目前为止所有被执行过的事务,主要用于复制中
我们也可以同如下命令查看
show master status;
当二进制日志被传输到备库后,会被储存在relay 日志中,从库会读取该GTID并设置 gtid_next变量为该GTID
这样就告诉备库下一个执行的事务必须为该GTID
需要注意的是该变量是session级别的
在接收到主库GTID事务并设置好gtid_next后,如果没有其他进程在执行的话,从库执行该GTID事务
如果同时有多个进程执行该事务,则会选择其中一个(如多线程复制)
我们可以查询gtid_owned系统变量来确认
select @@GLOBAL.gtid_owned
由于该事务已经被主库分配了GTID,所以从库上的该事务不会被分配GTID,而是使用gtid_next变量的值
由于从库重新执行了来自主库的事务,所以他也会写日志到从库的二进制日志文件中
这里分两种情况
需要注意的是如果未开启二进制日志功能MySQL 5.7及之前只有DML操作是原子级别的,DDL并不是,意味着如果MySQL发生异常,数据可能会变得不一致
MySQL 8.0后所有操作都支持
同样的在提交后,从库也会将该GTID写入到从库的gtid_executed系统变量中
如果启用了多线程复制(slave_parallel_workers > 0),由于是并行执行的GTID事务,每个线程负责不同的GTID,这时gtid_executed变量的值可能会有GAP,系统会自动更新这些值
所有的数据库更改(DML或DDL)都会被分配GTID
数据库的新增删除修改也会被分配GTID
非事务型的修改也会被记录下来,如果在写入过程中发生异常,则会记录一个incident事件
如下操作可能会让一个事务分配多个GTID
本专题内容翻译自官方文档并结合自己的环境
https://dev.mysql.com/doc/refman/5.7/en/replication-gtids-lifecycle.html