磁盘I/O瓶颈需先确认再优化:用iostat看r_await/w_await是否超10ms、iotop确认mysqld刷盘、SHOW GLOBAL STATUS查Innodb_data_reads激增及缓冲池命中率低于99%、ps查iowait>10%,再针对性调优缓冲池、redo log大小及SQL索引。

磁盘 I/O 是 MySQL 最常见的性能瓶颈,尤其在高并发或数据量大的场景下。优化的核心不是“压测调参”,而是让热数据尽量留在内存、让写操作尽量合并、让磁盘只做它擅长的事——顺序写。
怎么判断磁盘 I/O 真是瓶颈?
别一上来就改配置。先确认问题是否存在:
-
iostat -x 1看%util(机械盘有意义,SSD 忽略)和r_await/w_await(高于 10ms 就要警惕) -
iotop -oP确认是不是mysqld进程在刷盘,而不是备份或日志归档进程 -
SHOW GLOBAL STATUS LIKE 'Innodb_data_reads'和Innodb_data_writes暴涨,同时Innodb_buffer_pool_read_requests/Innodb_buffer_pool_reads的比值低于 99%,说明缓冲池命中率崩了 -
ps -eo pid,comm,pcpu,psr,iowait查看 mysqld 的iowait是否持续 >10%
innodb_buffer_pool_size 设置多少才合理?
这不是一个固定百分比问题,而是一个“是否够用”的问题。设大了挤占系统内存,设小了频繁换页,抖动比慢还伤人:
- 专用数据库服务器:设为物理内存的 60%~80%,但必须是
innodb_buffer_pool_instances的整数倍(比如设 8 实例,总大小就得是 8GB、16GB 这样的整数) - 动态调整需 MySQL 5.7+,执行
SET GLOBAL innodb_buffer_pool_size = 12884901888(12G),但注意该命令会触发缓冲池重建,期间可能短暂卡顿 - 监控真实效果:用
SHOW ENGINE INNODB STATUS查Buffer pool hit rate,低于 99.5% 就得查是不是有大表全扫、没走索引,而不是盲目加内存 - 启用预热:
innodb_buffer_pool_dump_at_shutdown = ON+innodb_buffer_pool_load_at_startup = ON,避免重启后首波查询全打磁盘
为什么调大 innodb_log_file_size 能降 I/O?
Redo log 不是越小越好。太小会导致检查点过于频繁,逼着 InnoDB 把大量脏页提前刷盘,形成随机写风暴:
- 建议单个
ib_logfile0设为 1~2GB(MySQL 8.0+ 支持在线调整,旧版本需停机) - 增大后,
Innodb_os_log_written增速变缓,Innodb_buffer_pool_wait_free下降,说明后台刷脏页压力减轻 - 注意:修改前必须先
SET GLOBAL innodb_fast_shutdown = 0,确保所有脏页落盘,否则启动可能失败 - 搭配
innodb_flush_log_at_trx_commit = 2使用更安全——log buffer 写 OS cache,每秒 fsync 一次,既避免每事务刷盘,又不会像=0那样丢数据
哪些 SQL 或配置会让 I/O 雪上加霜?
再好的硬件和参数,也扛不住坏 SQL 和错配的内存参数:
-
SELECT *+ 大表 + 没索引 → 全表扫描 → 大量随机读;ORDER BY字段无索引 →Using filesort→ 排序区溢出到磁盘临时表 -
Created_tmp_disk_tables / Created_tmp_tables > 5%?说明tmp_table_size和max_heap_table_size太小,该调大(按 session 级设置更稳妥) -
innodb_flush_method = O_DIRECT在 SSD 上推荐启用,绕过 OS page cache,避免双重缓存导致 swap;但在某些云盘或 NFS 上反而更慢,得实测 - 把
datadir、innodb_log_group_home_dir、tmpdir分开挂载到不同物理设备(哪怕只是不同 SSD 分区),能显著减少 IO 争用
真正卡住 I/O 的,往往不是单个参数,而是缓冲池不够 + 日志太小 + 慢查询没索引 + 临时表落盘这四件事同时发生。调参前,先用 EXPLAIN 和 slow_query_log 把最耗 I/O 的那几条 SQL 找出来——它们才是拖垮整个系统的“真凶”。











