模拟之前先来简单学习一下GTID的知识。
1. GTID的基本概念
GTID的全称为全局事务标识符(global transaction identifiner),是MySQL 5.6引入的一个特性。
GTID保证了MySQL的每一个事务都有一个全局唯一的标识,该标识在本实例甚至主从复制环境都保证全局唯一。
GTID = server_uuid:sequence_id
server_uuid是一个32字节+1字节(/0)的字符串,MySQL第一次启动时生成,并将该信息写入datadir目录下
的auto.cnf文件。如果该文件丢失,MySQL会重新生成一个新的server_uuid。相同的server_uuid下的事务
对应的sequence_id在binlog文件中是递增且连续有序的,他们以集合的方式呈现。
GTID = server_uuid:sequence_id GTID号示例:
2. GTID的优点
1 通过gtid定位该事务来自哪个实例。
2 搭建主从复制不再需要指定binlog文件以及具体的位点信息,而是通过全局唯一的gtid来做复制,MySQL通
过gtid来验证冲突并确保每个事务只会被执行一次。
3实现主从更简单,不用像以前一样寻找log_file和log_pos,而是使用 master_auto_position=1 的方
式自动匹配 GTID 断点进行复制。
4 比传统的主从更加安全,一个 GTID 在一个服务器上只执行一次,避免重复执行导致数据混乱或者主从不一致。
5 GTID是连续没有空洞的,保证数据一致性。
3. GTID生命周期 以Mysql5.7为例
1 当事务于主库执行时,系统会为事务分配一个GTID,(当然读事务或者被主动过滤掉的事务不会被分配GTID)
,写binlog日志时,此GTID标志着一个事务的开始。
2 binlog中写GTID的event被称作Gtid_log_event,当binlog切换或者mysql服务关闭时,之前binlog中
的所有gtid都会被加入mysql.gtid_executed表中。
3 当GTID被分配且事务被提交后,他会被迅速的以一种外部的、非原子性的方式加入 @@GLOBAL.gtid_executed
参数中,这个参数包含了所有被提交的GTID事务(其实他是一个GTID范围值),这个参数也被用于主从复制,
表示数据库当前已经执行到了哪个事务。
4 在主从首次同步时(master_auto_position=1),slave会通过gtid协议将自己已经执行的gtid set
(@@global.gtid_executed)发给master,master比较后从首个未被执行的GTID事务开始主从同步。
5 当事务随binlog被传输至slave后,slave每次读到Gtid_log_event就把自己的gtid_next参数设为此
GTID,需要注意的是 这里的gtid_next是在复制进程的session context中自动设置的(由binlog提供的
语句),这里的结果不同于show variables like 'gtid_next';是当前会话本身的gtid_next,
这是个session级别的参数。
6 同样的,在slave上如果开启了binlog,GTID也会以Gtid_log_event事件写入binlog,同时binlog切换
或者mysql服务关闭时,当前binlog中的所有gtid都会被加入mysql.gtid_executed表中。
7 在备库上如果未开启binlog,那么GTID会被直接持久化到mysql.gtid_executed表中,在这种情况下
slave的mysql.gtid_executed表包含了所有已经被执行的事务。需要注意的是在mysql5.7中,向mysql.gtid_executed
表插入GTID的操作与DML操作是原子性的,对于DDL操作则不是,因此如果slave在执行DDL操作的过程中异常中
断那么GTID机制可能会失效。在mysql8.0中这个问题已经得到解决,DDL操作的GTID插入也是原子性的。
4. 主从复制搭建需要关注的参数
server_id //主从库server-id必须不一样
log_bin //主库必须打开binlog日志
binlog_format=row // #bin_log日志格式,共有三种STATEMENT、ROW、MIXED
gtid_mode=on //开启gtid
enforce_gtid_consitency=on //on off WARN 保证gtid一致性
log_slave_updates //5.6版本中使用gtid必须开启该参数,5.7.5以后该参数决定gtid信息持久化介质是哪些
开始模拟:这里我们模拟一个主从复制架构中,从服务器中途异常宕机,不再同步主服务器的场景,假设该情况
宕机时间较长,数据量较大,GTID无法短时间恢复,并要求不停业务进行数据同步修复,恢复一致。
(或者说主服务器宕机,从服务器代替主服务器提供服务,现在要恢复主服务器的数据,使它与从服务器的数据同步)。
在主服务器中创建从服务器连接的用户
CREATE USER 'copy'@'176.104.XXX.XX' IDENTIFIED BY 'copy';
GRANT REPLICATION SLAVE ON *.* TO 'copy'@'176.104.XXX.XX';
flush privileges;
使用mysqldump使主从信息一致
主mysql执行:
mysqldump -uroot -p123 demo > dump2.sql
scp dump2.sql 172.16.104.XX:/
从mysql执行:
mysql> source /dump2.sql
信息一致,开启主从复制:
CHANGE MASTER TO
MASTER_HOST='172.104.**.*',
MASTER_USER='copy',
MASTER_PASSWORD='copy',
MASTER_PORT=3306,
MASTER_AUTO_POSITION=1;
start slave;
查看从库状态:show slave status\G
IO/SQL线程都为YES,开启成功。
在主库中添加数据测试,从库也同步到数据,主从一致。
编写脚本,设置数据随机插入模拟业务增长。 (插入时间间隔 < 5s), 利用Random函数
运行脚本并挂载到后台:nohup sh insert.sh &
查看主从数据:同步,业务模拟完成。
此时节点还在陆续插入
在这里模拟从库宕机(手动stop)
从库数据:276rows
主库数据:294rwos,发现数据不一致。到此模拟主从故障完成。
开始恢复数据
思路:
先通过mysqldump全量备份当前的数据,由于不能影响业务,所以在mysqldump数据时不能造成锁表。要保持
数据写入,由于mysqldump时数据还在写入,所以有一部分数据还是会同步不全,所以导入mysqldump的数据
后,跳过dump中包含的GTID事务,再重新建立一次主从配置,开启slave线程,恢复数据并同步。
mysqldump不锁表备份数据
mysqldump -uroot -p123 --single-transaction --master-data=2 -R demo > dump4.sql
主要起作用参数:--single-transaction:设置事务的隔离级别为可重复读,即REPEATABLE READ,这样能保证在一个事务中所有相同的查询读取到同样的数据,也就大概保证了在dump期间,如果其他innodb引擎的线程修改了表的数据并提交,对该dump线程的数据并无影响,在这期间不会锁表。
在从库中导入数据:
scp dump4.sql 172.104.**.*/d
mysql客户端内:
mysql> source /dump4.sql
此时从库中数据:
主库中数据,可以看出,数据仍然未同步。
由于我们mysqldump的数据已经包含了在MASTER执行的 1-274 个事务,所以我们在此时SLAVE进行同步的时候,要忽略这些事务不再进行同步,不然会出现类似于这种报错:
要想跳过某些GTID,SLAVE必须保证 gtid_purged 参数为空才能正确跳过,查看当前的gtid_purged:
当前gtid_purged不为空,所以我们要先设置它为空,执行:reset master;
查看gtid_purged
mysql> show global variables like '%gtid%';
reset master实际上做了以下操作:
将gtid_purged参数设为空字符
将gtid_executed设为空字符
清空mysql.gtid_executed表
如果DB server开启了binlog,那么reset master还会清除所有binlog文件和binlog index file,然后以初始的自增序列号1开启一个新的binlog
gtid_purged为空后,开始重置SLAVE
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> reset slave all;
Query OK, 0 rows affected (0.02 sec)
查看主库当前mysqldump导出数据的GTID号
重置后,设置跳过的GTID,并重新同步MASTER
mysql>SET @@GLOBAL.GTID_PURGED='c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-228';
Query OK, 0 rows affected (0.01 sec)
mysql>CHANGE MASTER TO MASTER_HOST='192.168.152.253',MASTER_USER='copy',MASTER_PASSWORD='copy',MASTER_PORT=3306,MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.04 sec)
开启SLAVE进程,查看同步状态
状态正常,在看一下数据。
在我们修改过程中插入的数据也已经全部同步。数据完全一致,主从复制修复完成。
GTID的限制
1. create table … select其实会转换为两个单独的事务,一个是create table,一个是insert数据。
当在事务中执行该语句时,一些情况下会导致两个事务分配了相同的事务id,这意味着从库会忽略掉插入数据
的事务,从而导致主从数据不一致
2.临时表的限制,只有在acticommit=1的情况下,才支持使用create temporary table和drop temporary
table语句。Master端创建临时表不产生GTID信息,所以不会同步到slave,但是在删除临时表的时候会产生
GTID会导致,主从中断.
3.主从库存储引擎不一致的情况下,会导致数据不一致
4.事务中包含对多个存储引擎的查询更新,会导致对该事务分配多个gtid
5.sql_slave_skip_counter传统模式的跳过position方式gtid模式下不支持。