背景:
经过实验,每次删除400万条要花1.5 - 3小时,而且是越到后面越慢,正常的话,需要大约102个小时,大约4天半时间。这在生产环境下是不能接受的。
产生原因:
每次删除记录,数据库都要相应地更新索引,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的;这是很慢的IO操作,而且后面索引碎片越来越多,就更慢,这就是为什么一开始只花1.5小时,后面要3小时才能删除400万条记录的原因。
【注意】记得在删除的时候不要在记录日志的模式下面,否则日志文件就要爆了。
方法一:
MySQL原文处理方案链接:https://dev.mysql.com/doc/refman/8.0/en/delete.html
简单翻译下:
删除大表的多行数据时,会超出innod block table size的限制,最小化的减少锁表的时间的方案是:
1、选择不需要删除的数据,并把它们存在一张相同结构的空表里 :INSERT INTO t_copy SELECT * FROM t WHERE … ;
2、重命名原始表,并给新表命名为原始表的原始表名: RENAME TABLE t TO t_old, t_copy TO t;
3、删掉原始表:DROP TABLE t_old;
方法二:
在实际操作过程中,如果不在乎时间(利用下班时间定时删除),可以利用limit分批次删除:
假设有一个表(syslogs)有1000万条记录,需要在业务不停止的情况下删除其中statusid=1的所有记录,差不多有600万条, 直接执行 DELETE FROM syslogs WHERE statusid=1 会发现删除失败,因为lock wait timeout exceed的错误。
因为这条语句所涉及的记录数太多,因此我们通过LIMIT参数分批删除,比如每10000条进行一次删除,那么我们可以利用MySQL这样的语句来完成:
DELETE FROM syslogs WHERE status=1 ORDER BY statusid LIMIT 10000;
然后分多次执行就可以把这些记录成功删除。
注:
- 执行大批量删除的时候注意要使用上limit。因为如果不用limit,删除大量数据很有可能造成死锁。
- 如果delete的where语句不在索引上,可以先找主键,然后根据主键删除数据库。
- 平时update和delete的时候最好也加上limit 1 来防止误操作。
***注:有参考其他论坛,博客,文章仅供自我学习;
***