在我们平常的项目开发过程中,这样的情况时有发生:前线用户向企业反馈,声称他们的应用程序数据出现问题。接着,业务团队就会将此问题交给开发人员来处理。经过详尽而艰苦的调查后,最后发现问题的根源在于软件处理不恰当所引发的脏数据问题。那么,面对这种情况,我们该如何解决呢?一般的处理步骤如下:
- 修复程序bug紧急发布hotfix版本
- 通过sql脚本快速修复脏数据, 这个修复时间越快越好
当表的数据量不大的时候,删除脏数据操作是一件比较容易的事,但是当表的数据量达到千万级别的时候,要想在不影响线上正常运行的情况下清理掉脏数据,不是一件简单的事,这个问题的难点在于:删除表数据不能锁表,我们都知道一条delete的sql语句如果查询条件不走索引会锁表的,即使查询利用到索引,也要考虑间隙锁的存在是否会影响现有的数据更新或新增的业务。
那么如何在不影响业务正常运行的情况下尽快修复数据呢,原则有两个:
分批删除
一次性删除大量数据可能会产生巨大的锁和日志,对数据库性能影响很大。一个更好的策略是每次只删除一部分数据,然后提交事务,再进行下一批。
使用索引
如果你正在根据某个字段(例如日期字段或ID)删除数据,确保该字段已经建立索引。否则,每次删除操作都可能需要全表扫描,这将极其耗费资源。使用索引可以大大提高 DELETE 操作的效率。
假设我们线上有一张用户订单表,我们要删除指定时间范围的数据,因为这段时间是新版本代码造成的脏数据,我们的delete的sql语句如下: delete * from orders where created_at >= '2010-10-01 13:00:00' and created_at <= '2010-10-10 12:00:00'。 我们有两种方案来达到删除指定时间范围的数据
方案一
- 给created_at 字段加索引
- 创建一个循环,使用limit每次在循环中删除1000条记录
方案二
- 把范围删除转为多条指定id记录的删除以此避免间隙锁的影响,转换的sql语句如下:
select concat('delete from orders where id=', id) as 'sql' from orders where created_at >= '2010-10-01 13:00:00' and created_at <= '2010-10-10 12:00:00';
- 将第一步返回的结果行(就是那些delete SQL语句) 拷贝到一个空的SQL文件temp.sql
- 执行第二步temp.sql