作者:赵黎明

爱可生 MySQL DBA 团队成员,熟悉 Oracle、MySQL 等数据库,擅长数据库性能问题诊断、事务与锁问题的分析等,负责处理客户 MySQL 及我司自研 DMP 平台日常运维中的问题,对开源数据库相关技术非常感兴趣。

本文来源:原创投稿

*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


背景

最近,IMG 的姜老师发布了一篇关于使用 gh-ost 会丢数据的文章(gh-ost 翻车!使用后导致数据丢失!),大致结论就是:在 MySQL AFTER_SYNC的 场景下,使用 gh-ost 进行表结构变更(包括最新 GA 的1.1.2版本在内),可能会导致数据丢失,还引起大家在微信群内展开了一些讨论。得知这个消息,还是觉得有些意外的,毕竟对于大部分 DBA 来说,gh-ost 属于比较常用的 DDL 工具,会用其替代 pt-osc 或 MySQL 自带的 online ddl 。出于好奇,去 gh-ost 的 Gtihub 主页上看了下,还真有相关的 issue ,并且已经有人提交了 fix 的 PR (目前该 fix 尚未得到官方回应)

根据文中提到的复现方法,我们来测试下,看看数据是不是真的会丢,会丢失到什么程度呢

环境准备

下载最新 GA 的源码包(v1.1.2)

git clone https://github.com/github/gh-ost.git

编译安装 gh-ost

安装很简单,官方提供了编译脚本,只需执行 master 目录下的 build.sh 即可,执行后会自动在 /tmp 下生成 gh-ost-release 目录,包括可执行二进制文件、二进制 tar 包、以及一个 gh-ostxxxxxx 的文件夹

搭建 MySQL 5.7 主从环境(1主1从)

搭建过程略…
确认从库复制线程正常

确认半同步状态正常

确认已配置AFTER_SYNC模式的半同步

开始验证

1、修改 gh-ost 源码文件./gh-ost-master/go/logic/migarator.go ,在 addDMLEventsListener 函数前增加一个等待时间,此处修改为 sleep 60s

2、在主库上配置半同步超时参数(注意,此处设置为一个大于 60s 的值,比如120000,即120s),为什么要设置大于 60s 呢?后面会解释

3、创建测试表并插入 1 条数据

zlm@10.186.60.62 [zlm]> show create table t\G
*************************** 1. row ***************************Table: t
Create Table: CREATE TABLE `t` (`id` int(11) NOT NULL,`name` varchar(10) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)zlm@10.186.60.62 [zlm]> insert into t(id,name) values(1,'a');
Query OK, 1 row affected (0.05 sec)zlm@10.186.60.62 [zlm]> select * from t;
+----+------+
| id | name |
+----+------+
|  1 | a    |
+----+------+
1 row in set (0.00 sec)

4、运行 gh-ost ,对测试表执行 DDL ,这里选择一个最简单的“alter table t engine=innodb;”进行测试

/tmp/gh-ost-release# ./gh-ost --execute --initially-drop-ghost-table --ok-to-drop-table --debug --allow-on-master --host=127.0.0.1 --port=3332 --user=zlm --password

命令执行后,会创建_t_gho、_t_ghc的中间表,随后进入 Listening 状态

用 watch 命令可以实时观察这 2 张表的变化

5、将从库上的 IO 线程停止(模拟半同步从库与主库通信异常)

zlm@10.186.60.68 [zlm]> stop slave io_thread;

6、主库执行 insert 语句(插入一条包含更大主键值的记录,如:id=2)

SQL 执行后会处于一直等待的状态,就像在等待事务锁释放一样

实际上,它是在等待从库的 ACK ,之前配置的半同步超时时间是 120s ,只有超过这个时间,主库才会降级为异步复制,并进行事务提交(innodb 引擎层)

下一秒,事务提交完毕后,对表的 DDL 变更也完成了,将原 t 表 rename 为_t_del表,将_t_gho表 rename 为新t表

由于 gh-ost 命令配置了 --ok-to-drop-table ,DDL 完成后会将_t_del直接删除(生产环境建议保留),最终仅剩下变更后的新 t 表

7、观察测试表数据变化

可以看到,这个事务最后是提交完成的,执行了1分56秒多,大致等于之前配置的半同步超时时间 120s ,但在完成 DDL 变更后,发现t表中并没有 id=2 的记录,即数据已经丢失了,测试结果符合预期

原理分析

之前在源码中配置了 sleep 60s ,从打印的日志中可以看到,在 gh-ost 命令执行1分钟后,即 16:37:23 开始进行了数据迁移(将原表数据写入影子表_t_gho),在获取原表数据时的取值范围是[1]…[1],id=2的新值(最大值)没有被获取到,为什么呢?

主库上配置了 autocommit=1 ,当事务语句执行后会自动进入提交流程(two-phase commit),解析 binlog 可以观察到事务写入 binlog 的具体时间,状态是 COMMIT 的,且包含 Xid ,而此时的 redo log 中,该事务仍然没有被“封口”,不能返回 ok 给客户端

由于主库配置的半同步超时时间大于gh-ost获取原表首尾记录临界值的开始时间(120s > 60s),gh-ost在读取Range时,主库事务还未真正提交(after_sync增强半同步要求至少有一个从库返回ACK后,才能进行引擎层提交,这是保证MySQL 5.7 Lossless semi-sync replicaiton的基础),gh-ost无法读取到这个新插入id=2的事务,最终导致这条记录丢失,如果这个新插入的事务包含N条记录,那么这N条记录都会一起丢失。

为何是60s?主要是为了便于观察和计算,日志上正好相差1分钟,当然也可以设置成30s,然后把半同步超时时间设置为40s、50s、60s等,经过多轮测试,只要这个时间小于半同步超时的时间,这个场景基本可以稳定复现,就不进行扩展了,感兴趣的小伙伴可以自行测试;相反地,当这个时间大于半同步超时的时间,则不会丢失数据,因为gh-ost在获取Range值时,事务在引擎层已经提交完毕

如何修复

Github 上已有人提供了修复方案,逻辑就是在 gh-ost 获取边界值 Range 的时候,加上一个共享读锁,并且加上了一个重试机制,示例如下:

修改./gh-ost-master/go/sql/builder.go 文件

修改./gh-ost-master/go/logic/migrator.go 文件

重新编译 gh-ost 后再次验证,同样是采用之前的配置,事务执行了1分53秒多,这次数据并没有丢失,看来修复方案有效

再来看下 gh-ost 打印的日志,在调整 Range 值得获取方式后,即使是 after_sync 模式下,也能使 Range 读取到正确的值,确保最终的数据一致性

注意事项

  • rpl_semi_sync_master_timeout 只需在主库修改即可
  • 需设置 rpl_semi_sync_master_wait_no_slave=on ,如果为 off ,即使停止从库 IO 线程,也不会出现等待 ACK 超时的现象,主库会直接降级,就无法观察到数据丢失了,之前没注意这个参数,以为是代码改错了,走了些弯路
  • 如果有多个从库,要考虑 rpl_semi_sync_master_wait_for_slave_count 的值对 ACK 的影响

总结

本文对使用 gh-ost 丢数据这一现象进行了测试,证明确有其事,不过要稳定复现需要满足一定条件,如:

  1. 使用 MySQL 5.7 及以上版本,并开启了 AFTER_SYNC 的半同步复制
  2. 半同步主库获取从库 ACK 超时并降级的时间晚于 gh-ost 获取 Range 值的时间
  3. 事务中包含 insert ,并且插入的值涉及到 according key 最大值的统计

最后通过修改源码的方式,测试了 fix 方案的可行性,鉴于 AFTER_SYNC 是目前普遍存在的应用场景,而 gh-ost 又是一款非常好用的工具,以上条件其实还是比较容易满足的,不知道的人会踩坑,因此,希望官方可以尽快采纳 fix 方案,并将其合并到后续 GA 版本中。

技术分享 | 实测在 after_sync 中使用 gh-ost是如何丢数据的相关推荐

  1. udp怎么保证不丢包_在 Flink 算子中使用多线程如何保证不丢数据?

    分析痛点 笔者线上有一个 Flink 任务消费 Kafka 数据,将数据转换后,在 Flink 的 Sink 算子内部调用第三方 api 将数据上报到第三方的数据分析平台.这里使用批量同步 api,即 ...

  2. 线程中如何使用对象_在 Flink 算子中使用多线程如何保证不丢数据?

    简介: 本人通过分析痛点.同步批量请求优化为异步请求.多线程 Client 模式.Flink 算子内多线程实现以及总结四部分帮助大家理解 Flink 中使用多线程的优化及在 Flink 算子中使用多线 ...

  3. 【华为云技术分享】开发团队中的任务没人领取,你头疼吗?

    背景 在传统开发模式下模下,发任务由项目经理指派给个人的,而在敏捷开发模式中,是由任务是项目经理指派给个人的,而在敏捷开发模式的务队领取的.很多企业开发任务的时候,有几个任务型中认领怎么办?问题:&q ...

  4. 【技术沙龙】星火计划 | 腾讯自研Kona JDK技术分享沙龙火热报名中

    导语:近几年,"Java已死"的论调时而出现.虽然一直被唱衰,但实际上,Java的生命力依旧旺盛.在TIOBE最新公布的3月编程语言排行榜中,Java热门程度仍然名列前茅,仅次于C ...

  5. 技术分享 | 漫谈音视频中的拥塞控制

    网络拥塞造成的丢包.延时和抖动,会导致实时音视频的质量降低.卡顿和时延增加,影响用户体验.本篇文章将介绍什么是网络拥塞,以及网络拥塞对音视频质量的影响,重点讲解在构建RTC系统时拥塞控制的一些策略. ...

  6. linux 监控新建进程,技术分享 | Linux 入侵检测中的进程创建监控

    作者简介:张博,网易高级信息安全工程师. 0x00 简介 在入侵检测的过程中,进程创建监控是必不可少的一点,因为攻击者的绝大多数攻击行为都是以进程的方式呈现,所以及时获取到新进程创建的信息能帮助我们快 ...

  7. hook 监控文件 c++_技术分享 | Linux 入侵检测中的进程创建监控

    作者简介:张博,网易高级信息安全工程师. 0x00 简介 在入侵检测的过程中,进程创建监控是必不可少的一点,因为攻击者的绝大多数攻击行为都是以进程的方式呈现,所以及时获取到新进程创建的信息能帮助我们快 ...

  8. 技术分享:前端开发中电脑和手机联调的方法

    如果是普通的H5页面,那么电脑和手机处于一个局域网,手机就可以访问电脑启动的前端项目了. 例如,项目地址是 http://192.168.43.241:8088,在手机直接访问http://192.1 ...

  9. 阿里IM技术分享(八):深度解密钉钉即时消息服务DTIM的技术设计

    本文引用自InfoQ社区"5亿用户如何高效沟通?钉钉首次对外揭秘即时消息服务DTIM"一文,作者陈万红等.策划褚杏娟,有修订和改动. 一.引言 本文是国内企业IM的事实王者钉钉首次 ...

最新文章

  1. jmeter启动警告项解决方案
  2. VIM编辑器(第十章)
  3. redis cluster 添加 删除 重分配 节点
  4. 头条python面试题_大意了,这几道Python面试题没有答对,Python面试题精选
  5. java 微信自定义菜单开发_微信公众号开发之设置自定义菜单实例代码【java版】...
  6. 无废话WCF系列教程 -- 李林峰
  7. 在java中使用JMH(Java Microbenchmark Harness)做性能测试
  8. 1.GD32F103配置环境,搭建工程
  9. android 传感器的学习
  10. mysql在故障切换时的事件调度器注意事项
  11. win10 动态磁盘 linux,win10 动态磁盘 无效,win10动态磁盘不能识别
  12. 我去!微信竟然可以查出行轨迹了,预计又一波情侣要分手?
  13. 软件项目管理六要素分别是什么?
  14. Apple开发账号添加团队成员
  15. 【Java】 实现一个简单文件浏览器(1)
  16. ubuntu中安装比较工具meld及其使用
  17. A tutorial on Principal Components Analysis - 主成分分析(PCA)教程
  18. 【初识】初学编程,望多指教
  19. Shell脚本采集基础监控项数据(含采集周期)
  20. 网易考拉Android客户端路由总线设计 1

热门文章

  1. 10_JavaScript数据结构与算法(十)哈希表
  2. 从长远来看读博的收益是巨大的,为什么读博的人却很少?
  3. CSP 202112-3 登机牌条码 40分
  4. 基于C51语音控制小车
  5. mysql odbc 驱动_mysql odbc驱动 32位 下载
  6. CNS无线电信号覆盖分析系统
  7. 生成所有的ALTERs,将所有MyISAM表转换为InnoDB
  8. 用过最好用的swf格式视频转换器,swf转成mp4
  9. OSG计算并绘制模型中每一个三角面片的法向量
  10. 【阿里云云计算工程师ACP认证】1.云服务器ECS