当前位置:首页>Linux>Linux磁盘IO优化实战:从调度器到文件系统

Linux磁盘IO优化实战:从调度器到文件系统

  • 2026-10-15 16:33:12
Linux磁盘IO优化实战:从调度器到文件系统

前言

服务器CPU不高、内存够用,但就是慢。top一看,%wa(IO wait)长期在50%以上,说明CPU在等磁盘IO。

磁盘IO慢的原因很多:调度器不合适、文件系统配置不当、硬件瓶颈、或者应用IO模式有问题。这篇文章从系统层面到应用层面,讲清楚怎么定位和优化。


一、IO性能指标:先看懂这些数据

1.1 基础指标

# 查看IO统计iostat -x 1# 输出示例Device            r/s     w/s     rkB/s     wkB/s   await  svctm  %utilsda              50.00   30.00   2048.00   1536.00  12.50  8.00   64.00

关键指标:

  • r/s、w/s:每秒读写次数(IOPS)
  • rkB/s、wkB/s:每秒读写数据量(吞吐量)
  • await:平均等待时间(毫秒),包括队列等待+服务时间
  • svctm:平均服务时间(毫秒),纯IO时间
  • %util:设备利用率,接近100%说明IO饱和

判断标准:

  • await > svctm:说明队列等待时间长,IO压力大
  • %util > 80%:IO设备接近饱和
  • svctm > 20ms:硬件可能有问题(SSD通常<1ms)

1.2 IO wait的含义

# top或htop查看top# 看 %wa 这一列# 或者用vmstatvmstat 1# 看 wa 这一列

%wa表示CPU等待IO完成的时间占比。如果%wa高但%util不高,可能是:

  1. 应用在等IO,但IO设备本身不忙(可能是网络IO或其他设备)
  2. 多个进程在等IO,但IO请求分散在不同设备

1.3 查看进程IO

# iotop查看进程IO(需要root)iotop# 或者用pidstatpidstat -d 1# 输出示例PID    kB_rd/s  kB_wr/s  kB_ccwr/s  Command12345  1024.00  512.00   0.00       java

二、IO调度器:选择合适的策略

2.1 查看当前调度器

# 查看调度器cat /sys/block/sda/queue/scheduler# 输出示例noop [deadline] cfq# 方括号表示当前使用的调度器

2.2 常见调度器对比

调度器
特点
适用场景
noop
简单FIFO,不做排序
虚拟化环境、SSD
deadline
按截止时间排序,避免饥饿
数据库、随机IO多
cfq
公平队列,按进程分配时间片
桌面系统、多进程混合IO
mq-deadline
deadline的多队列版本
现代SSD、NVMe

2.3 切换调度器

# 临时切换echo deadline > /sys/block/sda/queue/scheduler# 永久切换(CentOS/RHEL)# 在 /etc/udev/rules.d/60-io-scheduler.rules 添加:ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="deadline"# 永久切换(Ubuntu/Debian)# 在 /etc/default/grub 修改:GRUB_CMDLINE_LINUX="elevator=deadline"# 然后 update-grub 和 reboot

2.4 调度器参数调优

deadline调度器参数:

# 查看参数cat /sys/block/sda/queue/iosched/read_expirecat /sys/block/sda/queue/iosched/write_expirecat /sys/block/sda/queue/iosched/fifo_batch# 调整参数(单位:毫秒)echo 500 > /sys/block/sda/queue/iosched/read_expire   # 读请求截止时间echo 5000 > /sys/block/sda/queue/iosched/write_expire # 写请求截止时间

建议:

  • 数据库:用deadline,读截止时间500ms,写截止时间5000ms
  • SSD:用noop或mq-deadline
  • 虚拟化:用noop

三、文件系统优化

3.1 挂载选项优化

# 查看当前挂载选项mount | grep /dev/sda1# 优化后的挂载选项(ext4)mount -o noatime,nodiratime,data=writeback,barrier=0 /dev/sda1 /data# 或者写入 /etc/fstab/dev/sda1  /data  ext4  noatime,nodiratime,data=writeback,barrier=0  0  2

常用选项:

  • noatime:不更新访问时间,减少写IO
  • nodiratime:目录不更新访问时间
  • data=writeback:延迟写回,提高性能(有数据丢失风险)
  • data=ordered:先写数据再写元数据(平衡性能和安全性)
  • barrier=0:关闭写屏障,提高性能(有数据丢失风险)
  • nobarrier:同barrier=0

建议:

  • 数据库数据目录:noatime,nodiratime,data=ordered
  • 日志目录:noatime,nodiratime,data=writeback
  • 生产环境谨慎用barrier=0,除非有UPS

3.2 ext4文件系统优化

# 格式化时优化mkfs.ext4 -O ^has_journal -E lazy_itable_init=0,lazy_journal_init=0 /dev/sda1# 调整日志大小(默认128MB)tune2fs -J size=512 /dev/sda1# 调整保留空间(默认5%)tune2fs -m 1 /dev/sda1  # 保留1%

参数说明:

  • ^has_journal:关闭日志(性能最高,但崩溃可能丢数据)
  • lazy_itable_init=0:立即初始化inode表(格式化慢,但后续快)
  • -J size=512:日志512MB(大日志减少日志切换)

3.3 XFS文件系统优化

# 格式化时优化mkfs.xfs -d su=256k,sw=2 -l size=512m /dev/sda1# 挂载选项mount -o noatime,nodiratime,largeio,inode64 /dev/sda1 /data

参数说明:

  • su=256k,sw=2:条带大小256KB,条带数2(RAID优化)
  • -l size=512m:日志512MB
  • largeio:大IO优化
  • inode64:支持64位inode号

3.4 文件系统选择建议

文件系统
特点
适用场景
ext4
稳定、兼容性好
通用场景
XFS
大文件性能好
大文件存储、数据库
Btrfs
支持快照、压缩
需要快照的场景
ZFS
功能强大、资源占用高
企业级存储

四、应用层IO优化

4.1 减少IO次数

问题:频繁小文件读写

# 不好的做法:每次写一行for i in {1..10000}; doecho"line $i" >> file.txtdone# 好的做法:批量写入{ for i in {1..10000}; doecho"line $i"; done } > file.txt

问题:随机IO多

# 如果可能,改为顺序IO# 比如:先收集数据,再批量写入# 或者:用内存缓存,定期刷盘

4.2 使用异步IO

同步IO:read()/write()阻塞直到完成

异步IO:

  • Linux 5.1+:io_uring(高性能异步IO)
  • 传统:aio_read()/aio_write()(但文件IO支持不好)

io_uring示例(需要编程):

// 伪代码示例structio_uringring;io_uring_queue_init(32, &ring, 0);// 提交读请求structio_uring_sqe *sqe = io_uring_get_sqe(&ring);io_uring_prep_read(sqe, fd, buf, size, offset);io_uring_submit(&ring);// 等待完成structio_uring_cqe *cqe;io_uring_wait_cqe(&ring, &cqe);

4.3 数据库IO优化

MySQL:

# my.cnfinnodb_flush_log_at_trx_commit = 2  # 每秒刷日志(性能更好,但可能丢1秒数据)innodb_flush_method = O_DIRECT     # 绕过OS缓存innodb_io_capacity = 2000           # SSD设置为2000-4000innodb_read_io_threads = 4innodb_write_io_threads = 4

PostgreSQL:

# postgresql.confshared_buffers = 4GBeffective_cache_size = 12GBrandom_page_cost = 1.1  # SSD设置为1.1-1.5

4.4 日志IO优化

问题:日志频繁刷盘影响性能

方案:

  1. 异步日志:日志先写内存,定期刷盘
  2. 日志轮转:限制单个日志文件大小
  3. 日志级别:生产环境减少DEBUG日志
# 示例:Java Logback异步日志<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">    <queueSize>512</queueSize>    <appender-ref ref="FILE" /></appender>

五、硬件层面优化

5.1 RAID配置

RAID级别
读性能
写性能
容错
适用场景
RAID 0
高
高
无
性能优先,无容错需求
RAID 1
中
中
高
小容量、高可用
RAID 5
高
中
有
平衡性能和容量
RAID 10
高
高
高
性能和容错都要

建议:

  • 数据库:RAID 10
  • 日志:RAID 1或RAID 0(有备份)
  • 冷数据:RAID 5或RAID 6

5.2 SSD优化

# 查看SSD信息smartctl -a /dev/sda# 启用TRIM(自动回收)# ext4: mount时加 discard 选项# 或者定期执行: fstrim /data# 调整调度器echo noop > /sys/block/sda/queue/scheduler# 或echo mq-deadline > /sys/block/sda/queue/scheduler

5.3 多路径IO(MPIO)

如果使用SAN存储,配置多路径提高性能和可用性:

# 安装multipathyum install device-mapper-multipath# 配置mpathconf --enable --with_multipathd y# 查看多路径multipath -ll

六、实战案例:定位IO瓶颈

6.1 案例1:数据库慢

现象:数据库查询慢,%wa高

排查步骤:

# 1. 看IO统计iostat -x 1# 发现 %util 90%,await 50ms# 2. 看哪个进程在IOiotop# 发现MySQL进程IO高# 3. 看MySQL慢查询日志# 发现大量随机读# 4. 优化方案# - 调整innodb_buffer_pool_size(增加缓存)# - 优化查询(加索引、避免全表扫描)# - 调整调度器为deadline

6.2 案例2:日志写入慢

现象:应用日志写入慢,影响性能

排查步骤:

# 1. 看IO统计iostat -x 1# 发现写IO高,await 30ms# 2. 看进程IOpidstat -d 1# 发现Java进程写IO高# 3. 优化方案# - 日志改为异步写入# - 日志目录用data=writeback挂载# - 日志轮转,限制文件大小

6.3 案例3:文件系统慢

现象:文件操作慢,但硬件性能正常

排查步骤:

# 1. 看IO统计iostat -x 1# 发现IOPS不高,但await高# 2. 看调度器cat /sys/block/sda/queue/scheduler# 发现用的是cfq,不适合当前场景# 3. 优化方案# - 切换调度器为deadline或noop# - 调整挂载选项(noatime等)

七、跨网络IO测试与监控

7.1 场景:分布式存储性能测试

如果需要在多台服务器上测试IO性能,逐个SSH登录很麻烦。

解决方案:

  1. 跳板机:所有机器都能连接的中转服务器
  2. 组网工具:WireGuard、ZeroTier、星空组网等,把机器组成虚拟局域网

用组网工具后,不管机器实际在哪,都可以用虚拟内网IP直接访问。然后用Ansible批量执行IO测试:

# Ansible批量执行fio测试ansible all -m shell -a "fio --name=test --filename=/tmp/test --size=1G --rw=randwrite --ioengine=libaio --bs=4k --iodepth=32 --runtime=60 --time_based"# 或者用脚本批量收集IO统计for host in 10.0.0.{1..10}; do    ssh $host"iostat -x 1 10" > iostat_${host}.logdone

7.2 场景:跨网络存储访问

如果应用需要访问远程存储(NFS、CIFS等),网络延迟会影响IO性能。

优化方案:

  1. 本地缓存:减少网络IO
  2. 批量操作:合并小IO
  3. 网络优化:用组网工具优化网络路径,减少延迟
# 示例:NFS挂载优化mount -o rsize=1048576,wsize=1048576,hard,intr,timeo=600 /nfs/server /mnt/nfs# 参数说明# rsize/wsize: 读写块大小,默认32KB,可以调大到1MB# hard: 服务器不可用时挂起(不要用soft,可能丢数据)# timeo: 超时时间(0.1秒单位)

八、监控与告警

8.1 关键指标监控

# 脚本:监控IO指标#!/bin/bashwhiletrue; do    iostat -x 1 1 | grep -E "Device|sda" | awk '{print $10}' | tail -1    sleep 60done# 如果 %util > 80%,告警

8.2 Prometheus监控

# node_exporter 已经包含IO指标# 查询:node_disk_io_time_seconds_total# 查询:node_disk_read_time_seconds_total# 查询:node_disk_write_time_seconds_total

8.3 告警规则

# Prometheus告警规则groups:-name:disk_iorules:-alert:HighDiskIOUtilexpr:rate(node_disk_io_time_seconds_total[5m])>0.8for:5mannotations:summary:"磁盘IO利用率过高"-alert:HighDiskIOWaitexpr:rate(node_disk_io_time_weighted_seconds_total[5m])>0.5for:5mannotations:summary:"磁盘IO等待时间过长"

九、常见问题与注意事项

9.1 调度器切换不生效

# 检查是否真的切换了cat /sys/block/sda/queue/scheduler# 如果还是旧的,可能是:# 1. 设备被其他进程占用# 2. 需要重启# 3. 内核不支持该调度器

9.2 挂载选项不生效

# 检查当前挂载选项mount | grep /dev/sda1# 如果选项不对,可能是:# 1. /etc/fstab配置错误# 2. 需要重新挂载:mount -o remount /data

9.3 性能测试不准确

# 用fio做标准测试fio --name=test \    --filename=/tmp/test \    --size=1G \    --rw=randwrite \    --ioengine=libaio \    --bs=4k \    --iodepth=32 \    --runtime=60 \    --time_based \    --direct=1# 参数说明# --direct=1: 绕过OS缓存,测试真实磁盘性能# --iodepth: IO队列深度,模拟并发# --bs: 块大小,模拟实际IO模式

9.4 生产环境调优风险

注意事项:

  1. barrier=0或data=writeback:有数据丢失风险,需要UPS
  2. 调度器切换:可能影响现有应用,先在测试环境验证
  3. 文件系统参数:格式化后才能改,需要备份数据

建议流程:

  1. 测试环境验证
  2. 灰度发布
  3. 监控指标
  4. 回滚方案

总结

优化方向
具体措施
预期效果
风险
调度器
数据库用deadline,SSD用noop
IO延迟降低20-50%
低
挂载选项
noatime, data=ordered
减少写IO 10-20%
低
文件系统
XFS适合大文件,ext4通用
大文件性能提升30%+
低
应用优化
批量IO、异步IO
IOPS提升50%+
中
硬件优化
RAID 10、SSD
整体性能提升2-5倍
低(成本高)

核心思路:

  1. 先用iostat看整体IO情况
  2. 用iotop或pidstat定位IO大户
  3. 根据场景选择调度器和文件系统
  4. 应用层优化(批量IO、异步IO)
  5. 硬件层面优化(RAID、SSD)

注意事项:

  • 生产环境调优要谨慎,先测试再上线
  • 监控IO指标,及时发现问题
  • 跨网络IO测试用组网工具统一管理更方便

最新文章

随机文章