`

复盘一次数据修复

 
阅读更多

源起

      最近接到一次刷新线上数据库的任务。目标是对线上MYSQL数据库的订单表非重要字段进行数据修复。待修改的数据集合大概有130万条。

 

计划

       由于业务逻辑比较复杂,所以无法用几句SQL语句来解决问题。最终考虑使用java程序写的任务来进行这个操作。一是查询service已存在,如果使用python重新开发service,存在引入新问题的隐患,而且未必会快;二是目前的项目主要以java为主,代码管理里没有python的项目。

 

开始

      此项工作开始的前提,是已经通过修正业务代码,限制了错误数据的生产,堵住脏数据的源头。

STEP 1

      在开始编写手动触发的job任务前,我们使用SQL语句确认了候选的数据集,并验证查询的SQL语句是否使用了索引。然后定义修改数据的逻辑。这里并没有太大的工作量,原因是从堵住脏数据的新业务代码逻辑改造即可。

STEP 2

       在测试环境里测试job任务。测试环境中的候选集有5万,很短的时间内就修复完成。通过SQL语句去验证,也证实修复结果完全而有效。

STEP 3

       优化任务代码,发现使用的字典表数据在百行以内,但是几乎每次验证是否需要修改都需要查询字典表。因此将字典表一次性录入内存,方便数据的查询。

STEP 4

        使用预发布环境的计算资源进行修复数据,即不占用线上任务的计算资源,也无需重启线上任务。

 

结果

       正当我以为方案完美,并付诸实践。结局却并不完全。在后续的验证中,发现数百条数据并没有被修复,而且这些数据的类型各异,显然不是修改逻辑中遗漏的样子。即便我重新阅读自己的代码也无法知晓这些漏网之鱼的形成原因。

       于是我开始往日志里去寻根问底,幸好我在代码的适当位置安排了日志输出,为这里的工作预留了后手。我发现在任务顺利进行到一半的时候,开始陆续出现数据库访问的超时报警。而且随着任务的进行,超时情况更加的频繁。

        虽然我在一开始对查询语句进行了SQL 评估,确保索引的使用。但是太多的分页,导致数据库在后期的访问中表现不佳。如果再次评估方案,我会在外层再加一道循环,令数据通过时间维度再切分一次(比如按天)。这样百万的候选集会被时间再拆分成近百个的数据子集,查询压力将锐减。时间维度的选择可以使用SQL语句来验证一下。

 

SELECT year(CREATE_DATETIME) AS YEAR, month(CREATE_DATETIME) AS MONTH, week(CREATE_DATETIME) AS WEEK,
COUNT(*) AS NUM
FROM `MY_ORDER` 
WHERE CREATE_DATETIME>='2018-07-01'
GROUP BY year(CREATE_DATETIME), month(CREATE_DATETIME), week(CREATE_DATETIME)

 

 

心得

       这次的教训,让我意识到获取数据到方式不应该仅仅止步是否使用索引,还应该关注数据的切分方式。如果有条件的话,可以在测试阶段安排相当规模的压力测试,可能发现方案的不足之处。在代码的适当位置加日志,也许是留一条救命的线索。

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics