innodb执行delete大表时,核心开销不在删除数据本身,而是反复加载数据页到buffer pool、标记为脏页、触发刷盘(flush),导致缓冲池与磁盘io饱和。

DELETE大表时,InnoDB到底在忙什么?
不是“删数据”本身慢,而是InnoDB在后台反复加载、标记、刷脏页——整个过程把缓冲池和磁盘IO全占满了。一条DELETE FROM t WHERE id 执行时,InnoDB会:
- 遍历索引定位每条匹配记录(B+树多层跳转,页读取量远超预期)
- 把涉及的数千个数据页全加载进
buffer_pool(哪怕只改其中几行) - 对每条记录打上删除标记(
DB_ROW_DEL_FLAG),但不释放空间 - 触发大量脏页生成,随后被后台线程或内存压力强制
flush
这期间,innodb_buffer_pool_pages_dirty可能瞬间飙升,而innodb_io_capacity若没调高,刷脏页就会卡住所有写入。
DROP TABLE为什么比DELETE更伤IO?
DROP TABLE看似一步到位,实则要同步清理四类资源:表空间文件、二级索引、数据字典、redo log中的相关记录。当表达580GB时,它会:
- 扫描并清空整个.ibd文件对应的所有区(extent)元信息
- 逐个释放每个区的页缓存,触发成批
flush请求 - 在
redo log中写入大量DROP_TABLE_LOG日志,加剧日志写满风险 - 如果使用
innodb_file_per_table=OFF,还会污染共享表空间,影响其他表性能
尤其在机械盘或低IOPS云盘上,DROP的IO吞吐很容易打满,连带同实例其他库也卡住。
为什么主从延迟会雪崩?
批量DELETE产生的binlog不是“一条语句”,而是按行或按事务拆成海量event。主库写完后,从库必须串行回放这些event:
- 每条
DELETEevent都要走完整查询路径(解析→优化→执行→写relay log→应用) - 从库没有主库的写优化(如change buffer),所有更新都直写磁盘页
- 若从库配置了
sync_binlog=1或innodb_flush_log_at_trx_commit=1,刷盘压力进一步放大 - 一旦某次
flush耗时过长,后续event排队,延迟就指数增长
这不是网络问题,是单线程SQL线程被IO拖垮的真实表现。
真正能落地的规避方式有哪些?
别信“加索引就能快”,大表删除的核心矛盾是IO与内存争抢。有效做法只有三个方向:
- 用
pt-archiver分批删:每次DELETE LIMIT 1000+SLEEP 0.1,控制脏页生成节奏 - 换TRUNCATE或DROP分区:如果是按时间分区的表,直接
ALTER TABLE t DROP PARTITION p202301,IO开销下降90%以上 - 删完立刻收缩空间:
OPTIMIZE TABLE t或ALTER TABLE t ENGINE=InnoDB(MySQL 5.7+支持ALGORITHM=INPLACE) - 提前调参:把
innodb_max_dirty_pages_pct从默认75%压到50%,避免刷脏页被动爆发
最常被忽略的一点:DROP或TRUNCATE前,务必确认该表没被任何SELECT ... FOR UPDATE或长事务持有MDL锁——否则操作会卡在等待锁阶段,看起来像IO抖动,其实是锁阻塞。











