数据库IO瓶颈是线上系统最常见的隐性性能杀手。当SQL本身没有慢查询、索引也合理,但整体吞吐上不去,甚至出现周期性卡顿,大概率就是磁盘IO跟不上写入或读取节奏。本文围绕Linux下的iostat工具和MySQL Innodb的刷盘机制,讲清楚如何定位和解决这类问题。

一、用iostat看清磁盘真实负载
iostat是sysstat包里的命令行工具,用来报告CPU和块设备的输入输出统计。排查数据库IO瓶颈时,我们最关心的是磁盘设备的利用率、等待时间和吞吐量,而不是单纯看读写速率。
通常建议使用iostat -x 1这样的命令,每秒刷新一次扩展统计。其中几个核心指标需要重点理解:util表示设备带宽利用率,接近100%说明磁盘已经饱和;await是平均每次IO等待时间,包括队列等待和服务时间;svctm是服务时间;rkB/s和wkB/s是读写吞吐。如果util长期高于90%且await明显增大,基本可以确定IO成为瓶颈。
# 安装sysstat(CentOS) yum install -y sysstat # 每1秒输出一次扩展设备统计 iostat -x 1 # 只看设备sda,并显示时间戳 iostat -x -t /dev/sda 1
在真实案例中,一台MySQL从库使用普通SATA盘,iostat显示wkB/s仅80MB左右,但util已经是99%,await高达30毫秒。这说明磁盘自身处理能力弱,随机写无法线性扩展。此时单纯加内存不够,需要结合Innodb刷盘行为来分散写入压力。
另外要注意,云服务器的云盘 sometimes 会限制IOPS,iostat里的r/s和w/s如果撞到云厂商的配额上限,也会表现为util满。因此排查时要交叉对比云监控和iostat数据,避免误判为数据库自身问题。
二、Innodb刷盘策略如何影响IO
Innodb作为MySQL的默认存储引擎,采用缓冲池(buffer pool)缓存数据页,修改先在内存生成脏页,再由后台线程按一定策略刷到磁盘。这个“一定策略”就是刷盘策略,直接决定IO的形状。
关键参数包括innodb_flush_method、innodb_io_capacity、innodb_io_capacity_max和innodb_flush_neighbors。其中innodb_flush_method控制数据文件和日志文件怎么跟操作系统交互,比如O_DIRECT会绕过操作系统页缓存,避免双缓存;innodb_io_capacity告诉Innodb后台刷脏页时每秒大概能用多少IO能力,默认200在SSD时代明显偏小。
-- 查看当前Innodb刷盘相关参数 SHOW VARIABLES LIKE 'innodb_flush_method'; SHOW VARIABLES LIKE 'innodb_io_capacity'; SHOW VARIABLES LIKE 'innodb_io_capacity_max'; SHOW VARIABLES LIKE 'innodb_flush_neighbors'; -- 动态调整为SSD友好配置(需SUPER权限) SET GLOBAL innodb_io_capacity = 2000; SET GLOBAL innodb_io_capacity_max = 4000; SET GLOBAL innodb_flush_neighbors = 0;
如果io_capacity设置过低,脏页积累到阈值后触发尖锐的同步刷盘,iostat上就会看到每隔几十秒出现一次util 100%的毛刺,应用层表现为间歇卡顿。调大该值并关闭flush_neighbors(SSD无寻道成本,相邻页合并反而浪费IO),可以让刷盘更平滑。
innodb_flush_method在Linux下一般推荐用O_DIRECT,尤其是使用硬件RAID或LVM时,能减少一次内存拷贝。但某些网络存储环境用fsync更稳,需要结合fio等压测工具验证,不能盲目套用。
三、从指标到调整的闭环排查步骤
排查不应止于“看到IO高”,而要形成观测、推断、调整、验证的闭环。下面给出一套可操作顺序。
第一步,在数据库慢的时候开两个窗口,一个跑iostat -x 1,一个跑SHOW ENGINE INNODB STATUS,看Innodb的Buffer pool脏页比例和挂起IO。如果脏页高且OS层util也高,说明刷盘跟不上写入。第二步,确认磁盘类型,若是SSD,把io_capacity提到真实IOPS的50%左右。第三步,观察iostat毛刺是否消失,再用sysbench做混合读写压测。
# 安装sysbench后做10分钟混合读写测试 sysbench oltp_read_write --db-driver=mysql --mysql-host=127.0.0.1 --mysql-user=root --mysql-password=test --tables=10 --table-size=1000000 --threads=32 --time=600 run
调整过程中建议把innodb_io_capacity_max设为capacity的两倍,留给紧急情况(如脏页过多)的突发刷盘能力。同时开启innodb_buffer_pool_dump_at_shutdown避免重启后预热慢导致瞬时IO暴涨。
最后用表格对比常见磁盘下的推荐起步配置,方便照抄:
| 磁盘类型 | innodb_io_capacity | innodb_io_capacity_max | flush_neighbors |
|---|---|---|---|
| 机械盘 HDD | 200-500 | 800 | 1 |
| SATA SSD | 2000 | 4000 | 0 |
| NVMe SSD | 5000 | 10000 | 0 |
经过这样一轮排查和调优,原本周期性超时的订单库在写入峰值时段iostat的await从25ms降到2ms以内,慢查询清零。数据库IO瓶颈不是玄学,用对工具、理解刷盘原理就能稳定拿下。
iostatInnodb刷盘策略数据库IO瓶颈修改时间:2026-08-07 15:27:32