iops瓶颈是云数据库硬性限制,大批量dml会超限导致请求排队、查询变慢;需通过io监控指标(如page_writes、log_bytes_flushed/sec)而非仅slow_log定位,不同存储类型上限差异大,且并发下锁争用易放大i/o堆积。

因为大批量 UPDATE、DELETE 或 INSERT 会直接触发底层磁盘的随机写入和日志刷盘,一旦超过实例的 IOPS 上限,后续所有请求都会排队等待 I/O 资源,表现为查询变慢、连接堆积甚至超时——这不是 SQL 写得不好,而是被 IOPS 硬性卡住了。
云数据库的 IOPS 是硬限制,不是性能余量
云厂商(如阿里云 RDS、MongoDB 云盘版)对每个实例规格都设定了明确的 IOPS 上限,例如:2核4G 的 MySQL 实例可能只有 300 IOPS,而 UPDATE 一条带索引的记录平均消耗 2–5 次 I/O(数据页 + 多个索引页 + 日志写入)。这意味着每秒最多只能安全执行约 60–150 行更新。超出后,系统不会拒绝请求,而是让它们在 I/O 队列里等待,拖垮整体响应。
- 不同存储类型上限差异大:
ESSD云盘支持按容量弹性提升 IOPS,SSD云盘则与规格强绑定,升级前必须查清控制台标注的「最大 IOPS」值 - 副本集/分片集群中,主节点承担全部写入压力,从节点只读不缓解主节点 IOPS 压力
-
Write Concern设置为{w: "majority"}会强制等待多数节点落盘,进一步放大 I/O 延迟,不适合批量写场景
哪些 SQL 操作最吃 IOPS?
不是所有“大批量”都等价。以下操作单位行数的 I/O 开销显著更高:
-
UPDATE带WHERE条件但未命中索引:触发全表扫描 + 每行回表 + 多索引更新 → 单行可能消耗 10+ IOPS -
DELETE后未VACUUM(PostgreSQL)或未compact(MongoDB):旧版本数据残留,后续查询仍需跳过无效记录,增加 I/O 扫描量 -
INSERT INTO ... SELECT从大表拉数据:不仅写目标表,还频繁读源表(尤其没索引时),读写双高 - 事务内混合 DML:比如在一个事务里先
UPDATE再INSERT再DELETE,会锁住更多页面,延长 I/O 占用时间
怎么判断是不是 IOPS 卡住了?
别只盯着 slow_log 或 EXPLAIN,要直奔 I/O 监控指标:
- 看
IO_Throughput_Total_Kb是否持续接近实例上限(如 120 MB/s 对应约 15000 IOPS @ 8KB/IO) - 对比
Page_Reads和Page_Writes:若Page_Reads异常高,说明缓存不够,正在疯狂补页;若Page_Writes持续满载,大概率是批量写入压爆了 buffer pool 刷脏能力 - 查
Log_Bytes_Flushed/sec:该值长期 > 10 MB/s 且伴随延迟上升,说明事务日志写入成为瓶颈,尤其在innodb_flush_log_at_trx_commit=1时 - 注意:MongoDB 4.2 云盘版副本集实例的监控页上
IOPS 使用率永远显示为 0,此时必须结合mongostat的netIn/netOut和系统级iostat -dxm 1(如有权限)交叉验证
真正容易被忽略的是:IOPS 瓶颈往往藏在“看起来合理”的语句背后——比如一条加了索引的 UPDATE,在高并发下因行锁争用导致事务排队,最终把 I/O 请求在队列里堆成雪球。所以批量操作前,不仅要测单条性能,更要压测真实并发下的 I/O 吞吐拐点。











