不能直接用单条 DELETE 清理亿级数据,因为会引发长时间表锁、undo日志暴涨、主从延迟飙升、IO打满及事务回滚风险;必须分批删除,配合索引字段与LIMIT控制每批规模。

为什么不能直接用单条 DELETE 清理亿级数据
执行 DELETE FROM huge_table WHERE create_time 这类语句,在亿级表上大概率会卡死:InnoDB 要扫描全表匹配条件、每行写 undo 日志、持续持有行锁甚至升级为表锁,同时 binlog 和 redo log 疯狂刷盘。我见过最短的一次锁表是 17 分钟——还没删完就被运维强制 kill。更糟的是,如果中途失败,回滚可能比删除还慢。
DELIMITER + 循环 + LIMIT 是基础但必须做对的组合
存储过程里不能硬编码表名或时间条件,否则没法复用。关键点不是“能不能写”,而是“怎么写才不翻车”:
- 必须用
PREPARE+EXECUTE动态拼接 SQL,否则无法传入表名和字段名 -
LIMIT值建议从5000起步,SSD 环境可试20000,但要监控innodb_row_lock_time_avg - 每次
DELETE后加DO SLEEP(0.3),不加这个,高并发下 IOPS 容易打满 - 务必在
WHERE条件字段上建索引,没索引的LIMIT就是假分批——还是全表扫
示例片段(注意 @sql 拼接和引号转义):
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
ORDER BY
- 用
SELECT COUNT(*)预估总量,再按batch_size算最大轮数,避免无限循环 - 每轮执行前加
SELECT 1 FROM `table_name` WHERE `date_col` 判断是否还有数据 - 单次过程运行时长超过 300 秒就
LEAVE,防止阻塞其他定时任务 - 把
id范围拆成WHERE id BETWEEN ? AND ?比时间范围更稳定,尤其当时间字段有重复值时
生产环境最容易被忽略的三件事
第一,没关 autocommit —— 存储过程默认自动提交,但你得显式写 START TRANSACTION 和 COMMIT,否则每批都是独立事务,undo 日志不会及时回收;第二,没设 max_sp_recursion_depth,递归调用存储过程时容易爆栈;第三,忘了在事件调度器里用 ALTER EVENT ... DISABLE ON SLAVE,主从延迟大的时候从库会堆积大量未执行的清理事件。










