DELETE不是轻量操作,因会触发脏页刷新、undo/redo日志写入、binlog落盘及二级索引更新等多重IO行为,显著推高IOPS。

删除操作本身不直接“写磁盘”,但会触发大量后台IO行为——脏页刷新、undo日志写入、binlog落盘、索引节点分裂/合并,这些才是真正推高IOPS的元凶。
DELETE语句为什么不是“轻量操作”
很多人误以为DELETE FROM t WHERE id = 123只是删一行,实际执行链路远比这复杂:
- 先在缓冲池中定位并标记该行记录为“已删除”(逻辑删除),产生脏页
- 写入
undo_log以支持事务回滚和MVCC,必须同步刷到磁盘(尤其是innodb_flush_log_at_trx_commit=1时) - 若开启
binlog,还需写入mysql-bin.000xxx,且默认是sync_binlog=1,即每次提交都fsync - 如果表有二级索引,每条索引记录也要被逐个删除并更新B+树结构,可能引发页分裂或合并,产生额外物理读写
- 当缓冲池压力大时,InnoDB会主动刷脏页(
innodb_max_dirty_pages_pct触发),把刚删掉的页也一并写出
如何确认是脏页刷新导致IOPS飙升
重点看两个指标是否同步陡升:
-
innodb_buffer_pool_pages_dirty:当前脏页数量。突然下降 + IOPS飙升 → 很可能是批量刷脏 -
innodb_buffer_pool_write_requests和innodb_buffer_pool_reads:前者突增说明写压力大,后者若也同步涨,说明刷脏过程中还触发了冷数据重载(即缓存命中率崩了) - 用
SHOW ENGINE INNODB STATUS\G查FILE I/O段,关注pending normal aio reads/writes和os file reads/writes是否持续高位
临时验证方法:执行SET GLOBAL innodb_max_dirty_pages_pct = 0;(慎用!仅测试),观察IOPS是否立刻回落——若回落,基本坐实是脏页刷新主导。
日志写入才是IOPS的“放大器”
单条DELETE产生的redo日志量,往往远超其修改的数据体积。尤其在以下场景会被显著放大:
- 大事务删除:比如
DELETE FROM logs WHERE ts ,即使只删10万行,redo可能写入数百MB,且强制按innodb_log_file_size分段刷盘 - 高并发小删除:多个连接同时删不同行,redo日志无法合并,每条都走完整fsync流程,IOPS呈线性叠加
- 配置不当:
innodb_flush_log_at_trx_commit=1+sync_binlog=1组合下,一次提交至少触发2次磁盘fsync
可查状态:SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';,对比前后差值与时间窗口,算出每秒日志写入量(单位字节)。若超过磁盘吞吐能力(如普通云盘约100MB/s),就会排队阻塞,进一步拉高innodb_data_fsyncs计数。
容易被忽略的“隐性IO源”
除了显式的删除和日志,还有三类IO常被漏掉:
-
purge thread:真正物理清理已删除记录的后台线程。它不参与事务,但会密集读取undo页、遍历索引页、释放空间——表现为Innodb_purge_ops上升 +handler_delete增加 - 自适应哈希索引(AHI)重建:删除导致B+树结构变化后,AHI可能失效并重建,引发额外缓冲池扫描
- 统计信息自动更新:MySQL 8.0+在大批量DML后可能触发
ANALYZE TABLE,全表采样带来意外的物理读
这类IO不会出现在processlist里,只能靠performance_schema.file_summary_by_event_name或云厂商提供的IO归因工具(如阿里云DAS的“IO热点SQL分析”)定位。











