作者:芬达
“芬达的数据库学习笔记" 公众号作者,国内最活跃 mysql ocp 考试讨论群群主,群号:120242978 。
本文来源:原创投稿
* 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
高能警告,因作者能力有限,文章的分析过程没有基于 MySQL 源码,而是根据一些业界大佬的文章和官方文档整理和思考得出的结论,如果觉得说得有道理,请点个赞,如果觉得胡说八道请留言。本文的受众是至少已经入门 MySQL 的 DBA。
前言
MySQL5.7 默认参数下我们开启了半同步,在一个事务提交(commit) 的过程时,在 MySQL 层的 write binlog 步骤后,Master 节点需要收到至少一个 Slave 节点回复的 ACK (表示收到了binlog )后,才能继续下一个事务;
sync_binlog=1
innodb_flush_log_at_trx_commit=1
...(等等)
lossless semi-sync replication 、无损半同步、增强半同步,都是一回事,说的是 MySQL5.7 参数rpl_semi_sync_master_wait_point=AFTER_SYNC
设置下的半同步,他是默认值,解决高可用切换后的幻读问题。MySQL5.6 没有这个参数设置,这个版本在高可用切换后有概率出现幻读,产生数据丢失,那就是数据不一致。如没有特别指出,本文的"半同步" 均指的是无损半同步。
《如何正确地关闭 MySQL 数据库?99%的 DBA 都是错的!》
我先说答案,数据是一致的!但我这里说的数据一致,指的是,原主库上原先看到的数据库,在新主库(原备库)也是只能看到这些数据,不多也不少,也就是我指的是不会发生幻读。
为什么旧主数据多了?
这两张网上流传很广的图,大家如果觉得似曾相似,那必须是资深 DBA 了。我整合了一下,再结合八怪的高清大图(《深入理解MySQL主从原理32讲》专栏第15节),画了以下这张原理图:
sync_binlog=1
,从库到主库拉去 binlog 的时间点发生在binlog: fsync
后。这里是步骤解释:
sync_relay_log=10000
每 10000 个事务刷一次刷盘(因为我们只讨论主库 crash 的情况,这个从库 relay 刷盘行为我们不用管)rpl_semi_sync_master_wait_point=AFTER_SYNC
,所以在这个位置点开始等待 slave 节点完成"relaylog: write",然后返回个 ACK 给 master。sync_binlog=1
,表示事务提交之前,MySQL 都需要先把 BINLOG 刷新到磁盘innodb_flush_log_at_trx_commit=1
,表示在每次事务提交的时候,都把 log buffer (内存)刷到文件系统中(os buffer)去,并且调用文件系统的“flush”操作将缓存刷新到磁盘上去。
我认为这里指的是刷 redo prepare 状态,redo commit 状态是不需要刷盘的,这是安全的,并且减少一次 io。懂源码的大佬欢迎打脸。
主库崩溃后主从复制的行为,我直接引用丁奇的《MySQL 45讲》第十五章的文字,有兴趣的同学可以直接去阅读原文。
A 阶段对应的是我图的这个位置
如果主库在 A 阶段发生了 crash,丁奇大佬这么说:
B 阶段对应的是我图的这些位置,按丁奇的说法细分为三种可能,我单独标记了。
如果主库在 B 阶段发生了 crash,丁奇大佬这么说:
2(a) 这个情况下,主库承认 binlog 的数据,但在拷贝到从库时,我对丁奇大佬的说法又细分为两个阶段,分别是 2aa 和 2ab 阶段,如下图:
老主库的 uuid 下的 gtid 编号要多。
测试
实验设计:
搭建一主一从半同步复制数据库
sysbench 压测主库几十秒后
kill -9 主库 mysqld
修改 mysqld_safe 或者 systemd 设置,确保防止主库自动被拉起来。
查看主库 binlog,查看从库show slave status\G ,对比看两者 gtid 是否一致。
[root@192-168-199-132 3306]# mysql -uadmin -pGta@2019 -S /database/mysql/data/3306/mysqld.sock -e "show slave status\G" |grep "ffc43852-1d82-11ed-a65f-000c29375703"
mysql: [Warning] Using a password on the command line interface can be insecure.
Master_UUID: ffc43852-1d82-11ed-a65f-000c29375703
Retrieved_Gtid_Set: ffc43852-1d82-11ed-a65f-000c29375703:210837-283030
Executed_Gtid_Set: ffc43852-1d82-11ed-a65f-000c29375703:1-283030
[root@192-168-199-131 3306]# cat 16.txt | grep GTID |grep "ffc43852-1d82-11ed-a65f-000c29375703:28303"
SET @@SESSION.GTID_NEXT= 'ffc43852-1d82-11ed-a65f-000c29375703:283030'/*!*/;
SET @@SESSION.GTID_NEXT= 'ffc43852-1d82-11ed-a65f-000c29375703:283031'/*!*/;
SET @@SESSION.GTID_NEXT= 'ffc43852-1d82-11ed-a65f-000c29375703:283032'/*!*/;
SET @@SESSION.GTID_NEXT= 'ffc43852-1d82-11ed-a65f-000c29375703:283033'/*!*/;
为什么这么难模拟
造一个慢的 io 设备 (好像只能造读写都慢的情况,没关系我们仅需要利用他读慢这个特性)
模拟网络慢应该也能达到类似效果
为什么差异了三条 gtid 而不是一条?
这个可能和组提交有关,也可能和并发线程有关,这里不方便展开研究,这可能是另一篇文章。
主从数据不一致,如何修复
很多情况下,主库 crash 后,会被 mysqld_safe 或者 systemd 服务自动拉起来,如果数据库重启速度很快,其实不一定要切换,RTO 也能得到保证。旧主有多的 gtid 没关系,拉起来后,gtid 会自动同步到从库,数据就不会不一致啦!
我们知道 MHA 的切换的大概逻辑是这样的:
卸载旧主库 VIP
选主,选出有最新 gtid 的从库作为新主(我只有一个从库,就是选他了。。)
等待 sql thread 补齐数据(if 有复制延迟)
主从角色切换
io thread 级补数, ssh 到旧主拉取 binlog 补数(if 主库 binlog 和从库有 gtid 差异)
新主(原备库) 挂载VIP,去掉 read_only ,提供服务
But! 很多 DBA 都知道了, MHA 在 gtid 模式下有 bug,他 5 这个流程是不走的。我们大多数人用的都是 gtid 复制模式下的 MySQL,所以这种情况下,主从数据是有概率不一致的。
修复方法,从库补数,还是主库回滚,谁对谁错?
对于主从 gtid 差异的修复方法,居然分了两个派别:
从库补数据
主库回退数据
kill -9
这种情况下,mysql 客户端会返回 【ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query】。发生了这个报错,其实服务器是告诉你 “在执行查询的时候,你与服务器失去连接了”当你重新连上服务器后,你应该发起一次重连确认数据的。我举个例子假设你原本在主库上执行以下 SQL,一个叫 fander 的人给他老婆转账 10 万元。begin;
update accounts set amount = amount - 100000 where account_id=1 and name='fander';
update accounts set amount = amount + 100000 where account_id=2 and name='fander's wife';
commit;
所以这个转账成功与否,是 MySQL 告诉他。你从库补数,还是不补数,是数据库层决定,然后告诉客户即可。
如果是主库 mysqld crash 了,在新主提供服务之前,可以选择基于远程拷贝 binlog,把旧主日志补到新主。但 server crash 了,从库已作为新主提供业务了,这时旧主不能再往新主补数据了,这会造成数据不一致!这个场景下,旧主要加回集群只有两种选择:
回退多余 gtid 建立复制,加回集群 对新主的备份,重做旧主,作为从库建立复制,加回集群。
扩展思考
sync_binlog=1
的情况下不会。但sync_binlog !=1
的情况下,有两个原因会导致主库的 binlog 没有从库新。
binlog 不是每次事务都刷盘,主库挂了后,丢失一些事务。
总结
https://upload-images.jianshu.io/upload_images/7398834-4714e92068825403.png
https://mp.weixin.qq.com/s/uZ5cr0-bB6K3GbokBmORqA
https://mp.weixin.qq.com/s/1BuNsfVBRwnxe1BNjLeiWg
https://mp.weixin.qq.com/s/8JNv17llDfwMbTdBsTxhxQ
新特性解读 | MySQL 8.0 通用表达式(WITH)深入用法
社区近期动态