首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

数字 0 的秘密

本篇文章介绍了一类因为存储数据“0”导致的数据库集群故障。

回顾上篇文章:Mesos 实战之 Constraints

(封面图片源自:pixabay jplenio)

背景

线上数据一致性排查的时候,发现某数据库集群存在少量数据不一致的现象,可是该集群近期并没有进行过主库切换,也没有出现其他可能导致该集群数据不一致的故障。

经过仔细排查对比发现,该集群某表中一个数据类型为 double 的列,主库中存储的数据为 "-0",而从库同步的数据却为 "0",如此导致了在使用数据一致性工具检查的时候发现了数据不一致的 chunk。

提出问题

先在测试环境创建一张简单的表来进行数据测试。

问题 1:奇怪的 - 0

手动插入数据 - 0 问题复现失败,由于线上服务使用的 Prepared Statement 来进行数据操作,常规 Query 无法复现 - 0 的情况,于是采用 PS 的测试用例,成功复现 - 0,测试代码如下。

复现结果:

可以看到普通 Query 插入的值被转换为 0,用 prepared statement 形式插入的值为 - 0,而 - 0 并没有被同步到从库中,复现线上问题。

问题 2:为什么同步不一致

由于上面的实验过程,同步不一致的原因已经比较明确了。由于线上服务主从同步 binlog_format 设置的为 MIXED 格式,生成的 binlog 语句不再是 prepared statement 形式,在从库重放时 - 0 被转换成 0,导致数据不一致。

通过实验可以看到,修改 binlog_format 后同步一致。

源码分析

(Version MySQL-Server 5.5)

上面这两个问题,本质上都是由一个问题导致的,即 MySQL 中 Prepared Statement 的处理与普通 Query 的处理逻辑不同。我们可以开启 MySQL 的 trace log,结合源码了解这其中的原因。

查看 MySQL 源码结合 trace log,MySQL 中处理 Query 的入口为文件中的函数,该函数会获取 Query 语句,

并在该函数中对不同形式的 Query Statement 进行分发处理。

以下为部分 MySQL 源码:

继续追查,发现其最终使用的数据落盘的函数为,可以看到是没有做转换直接写到数据文件中的。

而 Query Statement 在解析过程中 double 类型的数据存在精度丢失,从而导致插入 - 0 时会得到 0。

结论

最终该问题的原因为以 Prepared Statement 形式的 SQL 在执行的时候数据直接落盘,未进行数据转换,-0 可以被写入到数据库中。

而在 binary log 的格式为MIX的时候,这一条 SQL 的 binary log 会被转换为为STATEMENT格式, 从库同步的时候以非 Prepare 的形式插入,精度丢失,导致从库插入的数据为 0,造成数据不一致。

而 ROW 格式的 binary log 记录了数据的变化,所以会把 - 0 完好的同步到从库当中。

参考资料

golang negative zero

MySQL prepared statemets

MySQL binary log setting

MySQL source code

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180417G19S0D00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券