MySQL执行DELETE需经历解析、权限校验、加锁、查记录、写redo/undo日志、标记删除、提交事务;InnoDB默认加next-key lock防幻读,索引类型影响锁粒度,删除不释放磁盘空间,需ALTER TABLE或分批删除。
DELETE 语句时到底发生了什么不是简单删掉几行数据就完事。MySQL 会走一套完整流程:解析 SQL → 检查权限 → 加锁 → 查找记录 → 写 redo log 和 undo log → 真正标记删除(或物理清除)→ 提交事务。这个过程直接受存储引擎影响,InnoDB 和 MyISAM 行为差异极大,而绝大多数生产环境用的是 InnoDB。
DELETE 的加锁行为与常见死锁场景InnoDB 默认在 DELETE 时对匹配的**聚簇索引记录加 next-key lock(间隙锁 + 记录锁)**,目的是防止幻读。这意味着即使你只删一条,也可能锁住一个范围。
DELETE FROM t WHERE status = 1,会锁住所有 status = 1 的记录及其前后间隙DELETE FROM t WHERE id = 100)→ 只加 record lock,不加间隙锁,锁粒度最小EXPLAIN 确认是否命中索引,否则先优化查询条件或补索引DELETE 后磁盘空间为什么没释放InnoDB 不会立即归还磁盘空间给操作系统。删除只是把记录标记为“已删除”,空间保留在页内供后续插入复用;只有当整页都空了,才可能被加入 FSEG_FREE 链表,但不会自动 shrink 数据文件(.ibd)。
ALTER TABLE t ENGINE=InnoDB 或 OPTIMIZE TABLE t
OPTIMIZE TABLE,它会锁表(8.0+ 支持 online DDL,但仍需注意 copy table 阶段 I/O 压力)WHERE id BETWEEN ? AND ?),并配合 sleep 控制节奏,避免长事务和日志暴涨DELETE 避免拖垮数据库直接 DELETE FROM huge_table WHERE create_time 很可能触发锁升级、undo log 膨胀、binlog 巨大、从库延迟飙升。
SELECT ... FOR UPDATE 先定位再删,避免重复扫描SHOW ENGINE INNODB STATUS 中的 TRANSACTIONS 和 LOG 部分,观察 undo log size 和活跃事务数ANALYZE TABLE 更新统计信息,避免后续查询走错执行计划DELETE FROM orders
WHERE id IN (
SELECT id FROM (
SELECT id FROM orders
WHERE create_time < '2022-01-01'
ORDER BY id
LIMIT 1000
) AS tmp
);
注意:MySQL 5.7+ 对这种子查询写法做了优化,但 5.6 及更早版本会报错 “You can’t specify target table for update in FROM clause”,得改用临时表中转。
最常被忽略的一点:DELETE 触发器、外键级联、FULLTEXT 索引更新、二级索引条目清理——这些
都会隐式增加开销,尤其是外键多且未建索引时,删父表一行可能引发子表全表扫描。