MySQL在SSD上必须实测调优innodb_io_capacity(设为fio随机写IOPS的0.6~0.8倍),并同步配置innodb_io_capacity_max(1.5~2倍)、innodb_flush_neighbors=0、innodb_write_io_threads≥4、innodb_lru_scan_depth≥2048,且关闭Linux I/O调度器(NVMe设none、SATA SSD设noop),否则参数无效。

MySQL在SSD上不调innodb_io_capacity,等于让后台刷脏页用HDD的节奏指挥NVMe的引擎——不是跑不快,是根本没给它发油门指令。
SSD的真实IOPS和innodb_io_capacity根本不是一回事
标称“50万IOPS”的NVMe盘,innodb_io_capacity设5000大概率会崩。因为它不控制redo log写入,只管后台刷脏页和合并change buffer的节奏。设太高,InnoDB就把IO通道全占了,前台事务卡在log file is full;设太低,脏页越堆越多,最后被日志切换强制flush,造成15分钟一次的IO毛刺。
必须用fio实测随机写能力,命令是:
fio --name=randwrite --ioengine=libaio --rw=randwrite --bs=4k --iodepth=64 --runtime=60 --time_based --direct=1 --filename=/path/to/ssd/testfile
重点看输出末尾的iops值(不是bw),取最后30秒稳定值。消费级NVMe常见8–15万,SATA SSD多在2–5万。
-
innodb_io_capacity= 实测IOPS × 0.6~0.8(写密集型用0.8,读多写少用0.6) - 例如实测115000 → 推荐设85000,不是115000
- 设成0或缺省,InnoDB会自动推算,但线上环境不可控,严禁依赖
单改innodb_io_capacity基本等于没改
只调这个参数,就像给快递站配了新调度表,却不增派车、不拆分区域、不关掉老式分拣机——包裹照样堵在门口。
必须同步生效的配套项:
-
innodb_io_capacity_max=innodb_io_capacity× 1.5~2(例如85000 → 设127500~170000);设为0或缺省会退化为自动计算,线上禁用 -
innodb_flush_neighbors = 0:SSD无寻道成本,合并邻近页纯属制造无效IO,关掉可稳提15%+吞吐 -
innodb_write_io_threads ≥ 4(建议设6~8):SSD并发能力强,线程太少直接饿着IO队列 -
innodb_lru_scan_depth ≥ 2048:默认1024太保守,SSD响应快,可以更激进扫描LRU链表
Linux I/O调度器不关,所有MySQL参数优化打七折
SSD不需要寻道,cfq或deadline调度器反而引入排队和延迟。不关它,innodb_io_capacity再准也是空转。
确认并设置方法:
- NVMe盘:
cat /sys/block/nvme0n1/queue/scheduler应含[none];没就执行echo none > /sys/block/nvme0n1/queue/scheduler - SATA SSD:
cat /sys/block/sda/queue/scheduler应为[noop](设备名按实际替换) - 云主机或容器里可能无法直接改,得查宿主机是否已配好
- 改完必须用fio压测对比,
await明显下降才算生效
怎么验证调参真起效了?别只盯QPS
关键看三个底层指标是否回归平稳:
- 执行
SHOW ENGINE INNODB STATUS\G,在“BUFFER POOL AND MEMORY”段里看Pages flushed速率是否连续稳定——突增突降说明节奏失控 - 监控
Innodb_buffer_pool_pages_dirty是否长期低于innodb_max_dirty_pages_pct(建议SSD下调至25~40),若仍高,优先调低该值,而不是继续拉高innodb_io_capacity - 查
/proc/diskstats对应设备的await是否持续%util 是否
最常被忽略的点:测试文件必须大于Buffer Pool,且不能放在系统盘或binlog目录下;否则你测的根本不是SSD,是瓶颈本身。


















