锁等待频繁本质是事务在等其他事务释放锁,而非DELETE本身慢;需查INNODB_TRX和INNODB_LOCK_WAITS定位静默持锁者,分批删除须带ORDER BY+索引字段。

锁等待频繁本质是事务在等其他事务释放锁
不是 DELETE 本身慢,而是它卡在等某个没提交的事务——比如一个凌晨跑的报表脚本只 START TRANSACTION 但忘了 COMMIT,或者某个微服务异常挂起,持锁几小时。这种“静默持锁者”在 SHOW PROCESSLIST 里看不到,必须查 INNODB_TRX 和 INNODB_LOCK_WAITS 才能定位。别急着 kill,先确认阻塞源是不是核心业务、有没有回滚中、上游会不会重试。
分批删必须带 ORDER BY + 索引字段,不能只靠 LIMIT
用 DELETE FROM t WHERE created_at 是危险的:优化器可能不走索引、可能漏删、并发下还可能重复删。正确姿势是:
-
ORDER BY id ASC或ORDER BY created_at ASC,且该字段必须有索引(主键最优) - 每次删完记录最后一条的
id值,下一批从这个值开始:WHERE id > 123456 AND id - 避免
LIMIT 10000, 5000这种 offset 分页写法——偏移越大越慢,且数据变动时逻辑错乱 - 每批独立事务,别包进一个大事务;建议每批 500–2000 行,视单行大小和日志容量调整
高并发下要主动控制节奏,别让 IOPS 把数据库压垮
连续执行几百批 DELETE,会把磁盘 IO、CPU、redo log 写满,连带拖垮线上查询。必须加休眠:
- 批次间
pg_sleep(0.2)(PostgreSQL)或客户端 sleep 100–500ms(MySQL) - 监控
performance_schema.replication_group_member_stats的COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE,超阈值就延长休眠 - 避开业务高峰执行;如果必须在线删,考虑降低 innodb_io_capacity,给读请求留余量
- 别依赖自动重试——应用层重试只会放大锁竞争,应设计成幂等+断点续删
真正容易被忽略的是“看不见的锁依赖”
加了索引、分了批、调了休眠,还是卡?问题常出在外部干扰:
- 某个监控脚本每 5 秒跑一次
SELECT COUNT(*) FROM t,拿的是共享锁,而你的 DELETE 在等排他锁 -
binlog_format = STATEMENT下,大事务重放极慢,主库删一半,从库延迟已超 2 小时 - 表上有全文索引(MySQL)或触发器(SQL Server),删一行实际要同步清理多个结构,持锁时间翻倍
- 使用 RCSI(Read Committed Snapshot Isolation)时,长时间未提交的事务会让版本链暴涨,DELETE 查可见性变慢
这些不会报错,但会让所有优化失效。删之前,先关掉非必要轮询、确认 binlog 格式、检查触发器和全文索引依赖。










