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

一个主键重复问题的分析

我们的一个应用刚用Mycat分库上线完成之后,第二天一大早,DBA就电话过来说一个分库表有重复主键(单个分片没有重复,不同分片间存在重复的情况);顺带的,DBA把重复的主键通过邮件发了过来。

点开邮件一看,总共有几十条主键重复的数据;而我们分库上线也才6、7个小时,在这段时间内,这个表总共有十几万的新记录生成。重复主键ID如下,仅截取两个,其他字段已略去。

我们在分库之后,分库表的主键ID通过Snowflake算法在应用本地生成,再进行insert;Snowflake的workerId通过应用服务器的IP哈希生成。

收到重复数据后,动手写了一个ID解码脚本,将重复的主键ID进行解码分析,解码后结果如下:

这些ID经过解码之后,time,workerId和seq都没有明显规律。十几万条新记录,只有几十条数据的主键ID是重复的,而且没有发现任何直接规律。第一感觉是不是ID生成器出并发bug了。Snowflake的ID生成器代码根据开源的源码修改而来,并没有做大的改动,而且又做了比较完善的测试,包括并发的测试。

还是不放心,重新review了ID生成的源代码和测试代码,又用不同的方式写了一个并发使用的测试用例,并加大了并发度;测试很多轮之后,依旧没有复现问题。

是其他地方出了问题吗?

又把重点放回到这几十个重复的ID上,重新分析了这些ID,发现这些ID的workerId都是同一个服务的多个应用服务器生成,但是并没有覆盖这个服务的所有服务器。而在生产环境只有两个服务会往这个分库表插入数据。另一个服务没有出现ID重复的情况。这两个服务分别部署了6台服务器。

于是重新检查了生成重复主键的那个服务的源代码,并在测试环境用大数据量验证,结果依然一切正常。

无奈,只能根据重复记录,与各服务器的日志进行对比;之所以一开始没有这么做,是因为这样做的工作量实在是太大;我们没有统一的日志中心,只能服务器一台一台的查找,而且每台服务器日质量又很大。不巧的是ID并没有输出到日志,只能根据其他字段查找。

通过重复ID的记录一翻辛苦的查找,终于找到一条重要线索。发生重复的两条记录并不是由同一个服务生成,而是两个服务各生成一条记录;ID相同,但是其他数据不一样。所以又把另一个服务的源码检查了一遍,果然发现了一个低级错误;该服务生成主键ID之后,并没有传入insert语句中,导致insert语句传入的是空对象。

为什么传入空对象会导致重复呢?

先确认了线上环境的各分库的表结构,ID列的AUTO_INCREMENT依然保留。查看MySQL官方文档[1],对自增长主键有如下描述:

When you insert any other value into an AUTO_INCREMENT column, the column is set to that value and the sequence is reset so that the next automatically generated value follows sequentially from the largest column value. For example:

即当对AUTO_INCREMENT列插入其他的值,插入的记录会使用传入的值,同时也会重置sequence的值,会根据当前列最大值来生成下一个sequence。

这么一来,整个问题终于完全清楚了。服务A使用应用生成的ID值插入分库表,这导致了MySQL表的sequence的值被重置。此后,服务B插入一条ID为空的数据,使用了自增长的sequence作为ID的值;而几乎与此同时,服务A正好生成了与自增长值相同的主键,并插入了数据库。所以这种重复才会是小概率的事件。

整个问题的解决花费了一些时间,也走了一些弯路。得到的教训是数据的线索对解决问题来说,比经验更重要;根据数据上的线索进行一步一步分析,才能高效的解决问题。比如一开始不要盲目的推测,把时间花费在Snowflake的算法代码上,而是直接根据重复数据去检索日志,这样会更快的找到问题。

另外,分库实施细节上也应该优化,比如,表分库后由应用生成ID,就把该字段的自增长的功能删除,这样,由空值插入就会立即报错,第一时间就能找到原因。

[1] https://dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html

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

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券