处理Redis和MySQL千万级数据不一致问题
背景
最近项目里一个大版本上线,其中商品服务对接了业务中台新的价格中心服务,接入了新的商品价格体系。
我们是面向C端用户的项目,出于接口性能的考虑,没有直接调中台的接口取价,而是将价格数据在我们这边也保存了一份,
存储有MySQL和Redis,取价主要通过项目内Redis获取。
上线时已跑了1次来源中台的全量价格,上线后增量价格修改主要走MQ消息推送来接收。
为了验证我们这边面向C端项目的价格跟中台那边的价格的一致性,BI大数据部门每天会抽取两边的数据进行对比,将有差异的价格
数据找出来,生成对比文件并发到钉钉群里供开发排查,数据来源主要是两边的MySQL表,如果没有差异,则在群里推送没有差异的消息。
上线跑全量价格数据时,是通过读文件的方式,拆分成多个文件由多个服务节点同时跑,数据批量保存到Redis,并且通过MQ消息异步保存至MySQL。
上线后的头几天,发现存在两边有数据差异的文件,经排查发现我们这边Redis实际是收到了商品价格数据,但是MySQL表里查不到。
当天处理方式为价格中心将差异数据通过增量推送再推一次。
在做对接时每个商品价格数据存储里设计了traceId
,通过traceId
跟踪,发现在全量的价格数据里其实是有的,那么说明当时通过MQ消息异步保存数据至MySQL时有遗漏。
(为什么数据有遗漏,经过排查和分析跟表的索引有关,以后单独写一篇博客来记录)
问题
上述是背景介绍,摆在面前的问题是,项目中Redis和MySQL表的数据不一致,数据量在5000w左右,需要把有差异的找出来,并更新成一致。
// 注:数据比对后有差异的数据虽然当天进行了处理,但之前的全量有遗漏的,而这边本身存在不一致情况,因此仍然需要单独处理。
思路
-
思路1:
编写程序通过scan
命令扫描Redis里所有的商品价格key,取出value将其中的业务识别字段转换成sql语句查询MySQL表,如果不一致,以redis为准修改表数据处理成一致。
该思路最容易想到,但由于数据量较大,每条数据通过sql查询并更新数据效率低,经过简单测试并计算预估时间可能需要跑几天。 -
思路2:
通过分析日志和数据发现,不一致的地方主要是数据在Redis里有,但在MySQL里没有。
MySQL查询表行数:select count(*) from xxx
Redis查看key个数:dbsize
或info keyspace
// 注:命令统计的是所有key,项目中价格数据是单独的Redis集群保存,并且大部分key都是价格数据,因此可这样统计。
两者对比数据量发现也是Redis数据多于MySQL表,大概多了8000行左右。
因此把Redis多的一部分数据找出来,通过insert新增到MySQL里即可。
考虑思路2,分步骤进行:
-
扫描Redis所有价格key
编写程序通过scan
命令扫描Redis里所有的商品价格key,取出来value中关键的业务字段,生成csv格式的文件
实际耗时:约30分钟 -
导入文件至MySQL临时表
项目使用阿里云RDS数据库,通过web界面提供的导入功能
实际耗时:约30分钟 -
找出差异数据并生成差异文件
编写sql将在临时表里有但原数据表里没有的数据找出来
实际耗时:1分钟内 -
编写程序读取差异文件更新MySQL数据
实际耗时:15分钟
总计处理时间(包含程序编写):约2小时内
经过处理后,BI数据门店在接下来的几天跑数据对比情况,我们跟中台的价格数据没有出现不一致的情况。至此,问题解决:)
总结
- 数据库跟缓存数据不一致是日常业务开发中经常面临和需要考虑的问题。
- 本项目是MySQL和Redis,由于异步保存产生的问题。
- 在对比数据一致性时,应该考虑数据和业务的实际情况,通过分治的思想,比如临时文件、临时表等方式,提高处理效率。