不能直接 delete from table where create_time,因为缺少比较操作符和具体时间值,语法不完整,无法执行条件删除。

为什么不能直接 DELETE FROM table WHERE create_time
直接删千万行会锁表(尤其 MyISAM 或未加索引的 InnoDB)、触发长事务、撑爆 undo log、拖垮主从延迟,甚至导致 OOM。MySQL 默认事务日志(innodb_log_file_size)和内存缓冲区(innodb_buffer_pool_size)根本扛不住单次大删。你看到的 Lock wait timeout exceeded 或 Deadlock found when trying to get lock,基本都源于此。
用 WHERE + LIMIT 分批删,但必须带主键/索引条件
分批删的核心是:每次只操作固定数量的行,且扫描范围可控。关键不是 LIMIT 本身,而是 WHERE 条件能否走索引——否则每次 LIMIT 都要全表扫,越删越慢。
- ✅ 正确写法(假设
id是主键,且时间字段有索引):DELETE FROM orders WHERE id
- ❌ 危险写法(无索引驱动,
LIMIT失效):DELETE FROM orders WHERE create_time —— 每次仍需扫描所有旧数据找前 1000 行
- ? 更稳妥的模式:用上一批最后的
id作为下一批起点,避免重复或遗漏:DELETE FROM orders WHERE id > 500000 AND id
删除期间如何避免主从延迟和连接堆积
高频小删看似安全,但若每秒执行几十次,binlog 写入压力、从库 SQL 线程重放负担、连接池耗尽风险反而更高。
- 控制节奏:每次删完
SLEEP(0.1)(单位秒),让从库追平、释放锁资源;生产环境建议SLEEP(0.3–1) - 避开业务高峰:用
SELECT COUNT(*)预估剩余量,结合SHOW PROCESSLIST观察是否有大量Waiting for table metadata lock - 别在事务里包多个
DELETE:每个批次独立提交,防止事务过长阻塞 DDL(如加索引) - 注意
max_allowed_packet:如果用程序拼接多条DELETE,超长 SQL 会被截断
真正省心的做法:用分区表 + DROP PARTITION
如果表按时间字段(如 create_time)做了 RANGE 或 LIST 分区,删历史数据就变成毫秒级元数据操作:
ALTER TABLE logs DROP PARTITION p_2021_q1, p_2021_q2;
前提是你建表时就规划了分区(PARTITION BY RANGE (TO_DAYS(create_time))),且 MySQL 版本 ≥ 5.7(8.0 更稳定)。没分区的表临时改?别试——ALTER TABLE ... REORGANIZE PARTITION 一样会锁表重建。
分区不是银弹:它要求查询也带上分区键才能裁剪,否则可能全分区扫描;而且 DROP PARTITION 后空间不会自动返还给操作系统(InnoDB 表空间仍是 .ibd 文件大小),需要后续 OPTIMIZE TABLE(同样锁表)。










