最近发生了一次 binlog 复制服务的一次故障, 记录一下.
0x00 背景
线上有个 MySQL 服务器, 通过 binlog 同步将数据更新发送到 Kafka, 供后续搜索等服务消费. MySQL 权限仅给了 db1
的所有 Table 的 SELECT
权限和 Slave 权限.
0x01 故障过程
由于要做线上 schema 变更, 因此同事在 test
库上作了一次预演:
- 创建了一个临时表
tmp_table1
- 完成线上 schema 修改的预演实验
- DROP 了上面的
tmp_table1
整个过程也就几分钟, 但对于 binlog 复制服务来说:
- 创建新的表, binlog 中收到了一个 TableMapEvent
- 然而所属的 MySQL user 并没有该表的 DESC 权限, 获取
tmp_table1
的详细信息时报错没有权限, 同步服务就此卡住 - 过了一段时间, 主从复制 lag 到达阈值, 触发 pagerduty 告警
- on-call 工程师接到告警, 查看日志定位到问题后, 试图通过赋予binlog 复制服务的 MySQL 账户
*.*
的SELECT
权限解决问题, 但tmp_table1
已经被DROP
掉, 因此 binlog 服务卡住了, 服务无法恢复 - 随后工程师决定跳过这些 binlog 的位置, 但 MySQL binlog 的 offset 是文件中的物理位置, 而不是类似 Kafka 的 offset 一样的类似日志 ID, 猜测了好几次才得以找到一个正确的 offset
整个过程下来, TTR(Time-To-Recover)估计有30分钟.
0x02 事后总结
整个事故有什么可以学习到的呢?
1. binlog 复制服务不适合的场景
所有的架构都是有适用场景的, binlog 复制也不例外, 可以学习到的是, 如果是那种经常 DROP
表的 MySQL 实例(少见, 但这次不就是构造了这种场景么), 在没有做 binlog 特殊处理的情况下是不适合的, 非常容易触发这种 binlog 复制服务找不到对应的表的情况
2. 线上数据库操作必须在严格的 staging 环境预演后再进行
线上数据操作, 在严格的
staging 环境预演, 注意是 严格的
staging 环境. 上述问题, 如果是随便 setup 的staging 环境, 有些细节跟线上不同, 就会失去提前暴露问题的机会. 通过自动化, 确保线上环境和 staging 环境除了数据和密码不一样, 其他都保持一致, 有时候是"救命"的
这个故障刚过, 当天夜里 MySQL --> Hive 的全量同步服务也出现了故障: 做线上 schema 更改, 使用的工具创建了_
开头的临时表在线上库, 但 _
开头的表在 Hive 中是违法的表名, 导致数据同步完成后在 Hive 中创建表失败. 一天两个故障, 身心俱疲[捂脸].
3. 监控一定要全面
出问题不可怕, 可怕的是"用户"比你先发现, 反过来通知你就尴尬了. 因此告警一定要覆盖全面, 一旦出现问题, 立马通知受影响的"用户"(内部系统或者外部用户). 欣慰的是, 这两次故障都是告警系统发现的.
0x03 Chaos Engineering
最后还是想, 还是要努力推进 chaos engineering, 平时通过错误注入触发错误, 再回头看自己系统的健壮性才是正解...
-- EOF --