Linux服务器磁盘I/O性能排查全流程:iostat到blktrace实战

Linux磁盘I/O性能瓶颈问题

服务器在高负载场景下经常出现磁盘I/O瓶颈:数据库查询变慢、日志写入阻塞、文件传输卡顿。典型表现是iostat%util持续接近100%,await值远高于正常水平。机械硬盘正常await值在5-15ms,SATA SSD在0.5-2ms,NVMe SSD在0.05-0.5ms。超出这些范围说明存在I/O性能问题。

排查磁盘I/O瓶颈需要一套系统化的诊断流程,从定位问题设备到分析根因再到实施优化。以下是基于实际运维经验的排查方法。

使用iostat定位I/O热点

iostat是Linux下最常用的I/O监控工具,来自sysstat软件包:

# 安装sysstat
apt install sysstat  # Debian/Ubuntu
yum install sysstat  # CentOS/RHEL

# 每2秒采样一次,显示扩展统计信息
iostat -dxm 2

# 输出关键字段说明:
# %util  - 设备利用率,持续>80%表示接近饱和
# await  - I/O请求平均完成时间(ms),含队列等待时间
# r/s    - 每秒读请求数
# w/s    - 每秒写请求数
# rkB/s  - 每秒读取KB数
# wkB/s  - 每秒写入KB数
# areq-sz - 平均I/O请求大小(KB)

%util高但rkB/s + wkB/s低时,意味着大量小I/O请求在排队。当areq-sz较小(如4KB)且r/s w/s很高时,属于典型的”大量小文件读写”场景。

要找出是哪个进程在产生I/O压力,使用iotop

# 按I/O速率排序显示进程
iotop -oP

# 或者使用pidstat按进程统计I/O
pidstat -d 2

blktrace深入分析I/O模式

当iostat只能看到设备级别的聚合统计时,blktrace可以追踪单个I/O请求的完整生命周期:

# 追踪sda设备的I/O事件
blktrace -d /dev/sda -o - | blkparse -i -

# 只追踪特定进程(pgrep获取PID)
blktrace -d /dev/sda -p $(pgrep -o mysqld) -o /tmp/trace.bin
blkparse -i /tmp/trace.bin

# 使用btrace一行命令完成追踪和解析
btrace /dev/sda

# 生成I/O时间线报告
blktrace -d /dev/sda -o /tmp/trace.bin -w 30
btt -i /tmp/trace.bin

btt输出的关键指标:

  • Q2Q:相邻I/O请求到达间隔,反映I/O生成模式
  • Q2C:从请求到完成的总耗时,等于等待时间+服务时间
  • D2C:设备实际服务时间,接近设备物理性能极限时此值会增大

/proc/diskstats实时监控

对于无法安装额外工具的生产环境,直接读取/proc/diskstats获取I/O统计:

# 查看sda1的统计信息
cat /proc/diskstats | grep sda1

# 字段说明(Linux 4.18+内核):
# 字段1: 主设备号
# 字段2: 次设备号
# 字段3: 设备名
# 字段4: 读完成次数
# 字段5: 合并读次数
# 字段6: 读取扇区数
# 字段7: 读总耗时(ms)
# 字段8: 写完成次数
# 字段9: 合并写次数
# 字段10: 写入扇区数
# 字段11: 写总耗时(ms)
# 字段12: 当前I/O请求数
# 字段13: I/O总耗时(ms)

编写简易监控脚本计算实时IOPS和吞吐量:

#!/bin/bash
DEV=sda
SECTOR_SIZE=512
PREV_READ=0
PREV_WRITE=0

while true; do
    STATS=$(grep "^ *.* $DEV " /proc/diskstats)
    READS=$(echo $STATS | awk '{print $6}')
    WRITES=$(echo $STATS | awk '{print $10}')
    
    if [ $PREV_READ -ne 0 ]; then
        READ_KB=$(( (READS - PREV_READ) * SECTOR_SIZE / 1024 ))
        WRITE_KB=$(( (WRITES - PREV_WRITE) * SECTOR_SIZE / 1024 ))
        echo "$(date +%T) Read: ${READ_KB}KB/s Write: ${WRITE_KB}KB/s"
    fi
    
    PREV_READ=$READS
    PREV_WRITE=$WRITES
    sleep 1
done

调优I/O调度器

Linux内核提供多种I/O调度器,适配不同硬件场景:

# 查看当前设备使用的调度器
cat /sys/block/sda/queue/scheduler

# 输出示例: noop deadline [cfq] bfq

# 临时切换调度器
echo mq-deadline > /sys/block/sda/queue/scheduler

# 永久配置(通过GRUB内核参数)
# 编辑 /etc/default/grub
# GRUB_CMDLINE_LINUX="elevator=mq-deadline"

调度器选择策略:

  • none/noop:无调度,请求按FIFO处理。适合NVMe SSD,避免额外开销
  • mq-deadline:多队列截止时间调度,保证请求在截止时间内完成。适合SATA SSD和大部分场景
  • bfq:按进程公平分配带宽,适合机械硬盘和桌面环境
  • kyber:自适应调度,根据设备能力调整。适合高性能NVMe

文件系统层优化

mount选项对I/O性能影响显著。以ext4为例:

# 查看当前mount选项
mount | grep sda1

# 性能优化mount选项
/dev/sda1 /data ext4 defaults,noatime,nodiratime,data=writeback 0 1

关键选项说明:

  • noatime:不更新文件访问时间,减少频繁小写入。对大多数服务器场景适用
  • data=writeback:元数据先写日志,数据不写日志。写入性能最好,但崩溃时可能丢失最近数据
  • data=ordered(默认):元数据写入前先刷入数据。安全和性能的折中
  • data=journal:数据和元数据都写入日志。最安全但写入性能最差

调整文件系统预读大小可以提升顺序读取性能:

# 查看当前预读值
blockdev --getra /dev/sda

# 设置预读为16MB(适合大文件顺序读取场景)
blockdev --setra 32768 /dev/sda

内核参数调优

调整脏页(dirty pages)比例直接影响写I/O行为:

# 查看当前配置
sysctl vm.dirty_ratio vm.dirty_background_ratio vm.dirty_expire_centisecs

# 生产环境推荐配置(写入/etc/sysctl.conf)
vm.dirty_background_ratio = 5
vm.dirty_ratio = 15
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500

dirty_background_ratio控制后台刷写触发阈值,dirty_ratio是阻塞写入的阈值。对于数据库服务器,建议降低这两个值,让数据更快落盘,同时避免突发大量I/O冲击磁盘。

常见I/O瓶颈场景与解决方案

数据库小随机读:表现为高IOPS低吞吐量。确保使用SSD而非机械硬盘,调整数据库的page size与文件系统块大小对齐,使用O_DIRECT绕过页缓存。

日志文件写入阻塞:日志进程出现I/O等待。将日志目录单独挂载到独立磁盘,使用异步写入方式,或接入消息队列做缓冲。

容器场景I/O放大:大量容器同时写日志导致overlay文件系统层产生额外I/O。配置容器日志驱动为json-file并限制大小,或使用fluentd收集后集中写入。

NFS挂载延迟高await值异常偏高。检查网络带宽和延迟,调整NFS的rsizewsize参数为1048576,启用NFS over RDMA。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linux-fu-wu-qi-ci-pan-io-xing-neng-pai-cha-quan-liu-cheng/

(0)
小编小编
上一篇 1天前
下一篇 1天前

相关推荐