Linux 使用内存缓存减少磁盘 I/O

前言

在 Linux 系统中,磁盘 I/O 往往是性能瓶颈。当应用程序频繁读取大文件(例如数据库文件、日志文件、多媒体文件)时,系统可能因为 I/O 等待而变得非常卡顿。为了减少磁盘压力、提升性能,Linux 提供了多种利用内存作为缓存的机制,例如:tmpfs、ramfs、Page Cache(页缓存)。本文将详细介绍这些机制的使用方法及区别,并介绍常用的磁盘 I/O 诊断工具。

磁盘 I/O 诊断工具

在 Linux 下,查看磁盘的吞吐量(每秒读 / 写的数据量)或者负载(IOPS - 每秒读 / 写的请求数),最常用、最直观的工具有下面几种。

工具是否需要安装能看具体磁盘是否推荐
iostat需要⭐⭐⭐⭐⭐
iotop需要间接⭐⭐⭐⭐
vmstat不需要⭐⭐⭐

iostat

iostat 是最快速的磁盘工具(强烈推荐)。

iostat 安装

1
2
3
4
5
# CentOS / RHEL
sudo yum install sysstat

# Debian / Ubuntu
sudo apt install sysstat

iostat 使用

  • iostat 的使用
1
iostat -x 1
  • 输出示例如下:
1
2
3
4
5
6
7
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
3.59 0.02 1.32 0.33 0.00 94.74

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sdc 0.09 0.67 33.60 14.91 2100.28 1028.30 128.99 0.32 6.53 2.01 16.73 0.43 2.09
sdb 0.00 0.00 0.17 0.00 9.39 0.00 110.24 0.00 6.75 6.75 0.00 4.22 0.07
sda 0.03 0.31 5.14 0.97 403.47 30.99 142.36 0.09 15.25 11.69 34.10 4.71 2.88
  • 重点关注字段
字段含义如何判断
%util磁盘忙碌时间百分比接近 100% = 磁盘 I/O 被打满(最重要)%util 最高的磁盘 = 当前 I/O 压力最大的磁盘
await平均 I/O 等待时间(ms)等待时间高 = I/O 排队严重
r/s每秒读的次数读的频率,高表示随机读多
w/s每秒写的次数写的频率,高表示随机写多
rkB/s每秒读取的数据量读吞吐量,顺序读场景通常很高
wkB/s每秒写入的数据量写吞吐量,顺序写场景通常很高
  • 其他字段说明
字段含义如何判断
rrqm/s每秒被合并的读请求数值高说明内核在合并顺序读
wrqm/s每秒被合并的写请求数值高说明顺序写、写合并效果好
avgrq-sz平均每次 I/O 请求大小(KB)值小(如 < 16KB)多为随机 I/O;值大多为顺序 I/O
avgqu-sz平均 I/O 队列长度值大于 1 则说明开始排队;持续偏大 = 磁盘处理不过来
r_await读请求平均等待时间(ms)读延迟高,说明读取受阻(数据库、随机读场景重点看)
w_await写请求平均等待时间(ms)写延迟高,常见于日志、落盘、fsync 场景
svctm平均 I/O 服务时间(ms)单次 I/O 被磁盘处理的时间(老指标,仅作参考)
  • 实战判断口诀
    • 磁盘是否被打满?
      • %util
    • 磁盘为什么慢?
      • %util + await + avgqu-sz
    • 随机 I/O 还是顺序 I/O?
      • avgrq-sz + r/s / w/s
    • 是读慢还是写慢?
      • r_await vs w_await
    • 吞吐瓶颈还是 IOPS 瓶颈?
      • rkB/s / wkB/s vs r/s / w/s

iotop

iotop 是磁盘交互式神器(看进程 + 磁盘)。

iotop 安装

1
2
3
4
5
# CentOS / RHEL
sudo yum install iotop

# Debian / Ubuntu
sudo apt install iotop

iotop 使用

  • iotop 的使用(需要 root 权限)
1
sudo iotop
1
2
3
4
5
# 或者
# -o 只显示有 I/O 的进程
# -p 显示进程而非线程
# -a 累计 I/O
sudo iotop -oPa
  • 输出示例如下:
1
2
3
4
5
6
7
Total DISK READ:  12.34 M/s | Total DISK WRITE: 56.78 M/s
Actual DISK READ: 12.10 M/s | Actual DISK WRITE: 55.90 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
12345 be/4 mysql 1.20 M/s 35.60 M/s 0.00 % 92.34 % mysqld
23456 be/4 root 8.50 M/s 0.00 B/s 0.00 % 48.12 % tar czf backup.tar.gz
34567 be/4 www-data 0.00 B/s 15.80 M/s 0.00 % 61.45 % php-fpm
987 be/4 root 0.00 B/s 2.10 M/s 0.00 % 5.23 % jbd2/sda1-8
  • 字段说明
字段含义说明
Total DISK READ总读请求速率所有进程发起的磁盘读取速率(总读请求速率)
Total DISK WRITE总写请求速率所有进程发起的磁盘写入速率(总写请求速率)
Actual DISK READ实际读速率实际落盘的磁盘读取速率(受页缓存命中情况影响)
Actual DISK WRITE实际写速率实际落盘的磁盘写入速率(受页缓存、写合并等机制影响)
TIDThread ID 线程 ID(比 PID 更细粒度)
PRIOI/O 优先级be = best effort(普通优先级)
USER进程所属用户进程运行的用户
DISK READ磁盘读取速率该进程每秒从磁盘读取的数据量
DISK WRITE磁盘写入速率该进程每秒写入磁盘的数据量
SWAPIN换页占比因内存不足导致的换页 I/O 百分比
IO>I/O 等待占比最重要:进程被磁盘 I/O 阻塞的时间比例
COMMAND命令名进程 / 线程名称
  • 排查磁盘 I/O 瓶颈时重点关注的字段
    • IO>(最重要)
      • 举例:
        1
        IO>  92.34 %
      • 含义:
        • 进程 92% 的时间在等磁盘 I/O
        • 几乎可以确定:磁盘是瓶颈
      • 判断规则:
        • IO> > 30% → I/O 明显慢
        • IO> > 60% → 严重 I/O 阻塞
        • IO> > 80% → 磁盘被打爆
    • DISK READ / DISK WRITE
      • 举例:
        1
        DISK WRITE 35.60 M/s
      • 用来判断:
        • 磁盘是读取多,还是写入多
        • 磁盘是否有异常的大流量进程
      • 特别注意:
        • 磁盘吞吐量高 ≠ 磁盘一定慢
        • 必须结合 IO> 来判断才有意义
    • Total / Actual 顶部汇总行(系统级)
      • 举例:
        1
        2
        Total DISK READ:  12.34 M/s | Total DISK WRITE: 56.78 M/s
        Actual DISK READ: 12.10 M/s | Actual DISK WRITE: 55.90 M/s
      • 字段:
        • Total DISK READ:所有进程发起的磁盘读取速率(总读请求速率)
        • Total DISK WRITE:所有进程发起的磁盘写入速率(总写请求速率)
        • Actual DISK READ:实际落盘的磁盘读取速率(受页缓存命中情况影响)
        • Actual DISK WRITE:实际落盘的磁盘写入速率(受页缓存、写合并等机制影响)
      • TotalActual 两者差距大的可能原因
        • Page Cache(页缓存)命中高
        • 写请求被缓存 / 合并

vmstat

vmstat 无需安装,缺点只能看整体的磁盘 I/O 吞吐量,看不到具体的磁盘。

vmstat 概述

  • vmstat 适合用来判断的事情
适用场景用法
是否有磁盘读写行为bi / bo 是否为 0
是否突然爆发 I/O 看是否瞬间飙升
写入型 / 读取型的吞吐量对比 bi vs bo
  • vmstat 不适合用来判断的事情
不适用场景原因
哪块磁盘 I/O 最高看不到具体磁盘
磁盘是否打满没有 %util
I/O 是否排队没有 await

vmstat 使用

  • vmstat 的使用
1
vmstat 1
  • 输出示例如下:
1
2
3
4
5
6
7
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 24642772 6851660 22099404 0 0 50 16 133 71 3 1 95 0 0
0 0 0 24644776 6851660 22096364 0 0 0 37 10080 9298 1 0 98 0 0
0 0 0 24644308 6851660 22096384 0 0 0 409 3063 8706 1 0 99 0 0
0 0 0 24652476 6851660 22096388 0 0 0 226 3145 8780 1 0 99 0 0
0 0 0 24640804 6851660 22096400 0 0 0 0 4060 10007 1 1 98 0 0
  • 重点关注字段

    • bi(Block In)
      • 每秒从块设备读取的数据量(磁盘读流量),单位:KB/s
      • 不是针对某一块磁盘,而是所有块设备的读吞吐量总和
      • bi 高,表示系统正在大量从磁盘读取数据
    • bo(Block Out)
      • 每秒写入块设备的数据量(磁盘写流量),单位:KB/s
      • 不是针对某一块磁盘,而是所有块设备的写吞吐量总和
      • bo 高,表现系统正在大量向磁盘写数据
  • 使用注意事项

    • vmstat 关注的是「磁盘吞吐量」,而不是「磁盘负载」
    • bi / bo 高 ≠ 磁盘一定忙
    • 磁盘顺序读写时:
      • bi / bo 很高
      • 但磁盘可能很轻松
    • 磁盘随机读写时:
      • bi / bo 不高
      • 磁盘却可能被打满
    • 不能用 vmstat 来判断磁盘 I/O 瓶颈
      • bi / bo 是全系统汇总值
      • 只能判断 “有没有磁盘读写”
      • 不能判断 “哪个磁盘最忙”
      • 不能判断 “某个磁盘是否打满”

磁盘 I/O 优化方案

tmpfs

tmpfs 的概述

  • tmpfs 是一种基于内存的文件系统:

    • 文件实际存储在内存 + 交换分区(Swap)中
    • 支持容量限制(挂载时可指定大小)
    • 内存不足时,tmpfs 文件可以被交换到 Swap
    • tmpfs 的文件内容本质上就是 Page Cache
    • 不会导致 OOM(Out of Memory)
    • 适合存放临时文件、缓存数据
    • 典型的挂载点:/dev/shm/run
  • tmpfs 的主要特点

    • 支持创建目录(无限层级)
    • 支持普通文件
    • 支持符号链接、硬链接
    • 支持权限、UID/GID
    • 支持文件删除(立即释放内存)
    • 支持调整大小(动态扩缩)
    • 系统重启后会丢失所有数据
  • tmpfs 的适用场景

    • 程序临时文件
    • 加速频繁读写的小文件
    • 构建系统(makenpmcargo 等)
    • OBS、FFmpeg 等读取多媒体文件减少磁盘压力

tmpfs 的使用

  • 创建挂载点
1
sudo mkdir -p /mnt/ramdisk
  • 挂载 tmpfs
1
2
# 比如限制 4GB 容量
sudo mount -t tmpfs -o size=4G tmpfs /mnt/ramdisk
  • 验证挂载
1
df -h /mnt/ramdisk
  • 开机自动挂载(可选)
1
2
3
4
5
# 编辑 fstab 系统配置文件,添加以下配置内容
sudo vim /etc/fstab

# 挂载 fstab 中所有自动挂载的文件系统,并检测配置是否正确
sudo mount -a
1
tmpfs /mnt/ramdisk tmpfs defaults,size=4G 0 0
  • 卸载挂载(可选)
1
sudo umount /mnt/ramdisk

ramfs

ramfs 的概述

  • ramfs 是最简单的内存文件系统:

    • 数据存储在纯内存中
    • 没有容量限制
    • 内存写满后系统不会阻止继续写入,会持续占用内存
    • 内存耗尽就会 OOM,最终导致系统宕机
    • 更像一个危险,但读写速度极快的文件系统
  • ramfs 的主要特点

    • 支持创建目录(无限层级)
    • 支持普通文件
    • 支持符号链接、硬链接
    • 支持权限、UID/GID
    • 支持文件删除(立即释放内存)
    • 不支持大小限制(不能设置 Size,容量可无限增长)
    • 数据页不可回收(不会被内核回收)
    • 不支持 Swap Out(永远驻留物理内存中)
    • 容易导致系统 OOM(写入越多,内存占用越多)
    • 系统重启后会丢失所有数据
  • ramfs 的适用场景

    • 极端场景下的高性能缓存
    • 只用于开发 / 研究,不建议生产环境使用

ramfs 的使用

  • 创建挂载点
1
sudo mkdir -p /mnt/ramdisk
  • 挂载 ramfs
1
sudo mount -t ramfs ramfs /mnt/ramdisk
  • 开机自动挂载(可选)
1
2
3
4
5
# 编辑 fstab 系统配置文件,添加以下配置内容
sudo vim /etc/fstab

# 挂载 fstab 中所有自动挂载的文件系统,并检测配置是否正确
sudo mount -a
1
ramfs   /mnt/ramdisk   ramfs   defaults   0   0
  • 卸载挂载(可选)
1
sudo umount /mnt/ramdisk

特别注意

由于 ramfs 没有容量限制,如果程序写入大量数据会导致系统 OOM。

Page Cache

Page Cache 的概述

即使用户不主动使用 tmpfs 或 ramfs 文件系统,Linux 内核本身也会自动利用内存作为 “磁盘缓存”。当用户读取一个文件时:

  • 内核将数据加载到操作系统的 Page Cache(页缓存)
  • 下次读取同一个文件时,不再触发磁盘 I/O,直接从内存返回结果

Page Cache 的使用

  • 将文件写入 Page Cache
1
cat video.mp4 > /dev/null
1
2
3
4
上述命令会:
- 强制把 `video.mp4` 文件从磁盘加载进内存
- 但不输出任何内容
- 后续程序读取 `video.mp4` 文件会命中缓存,不再产生磁盘 I/O
  • 查看缓存使用情况
1
2
# 查看内存使用情况,会看到 buff/cache 部分增大
free -h
  • 清除缓存(慎用,仅测试使用
1
2
3
4
5
# 将所有文件系统的缓存数据写回磁盘
sudo sync

# 清理页缓存、目录缓存和 inode 缓存
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

特别注意

在 Linux 生产环境中,不建议随意清缓存,否则会影响系统性能。

优化方案总结

  • tmpfsramfs、Page Cache 的优缺点
技术优点缺点适用场景
tmpfs 快、可限制容量、安全占用系统内存缓存文件、临时空间、多媒体文件读取
ramfs 极快不限容量,OOM 风险高测试、高速缓存(不推荐生产环境使用)
Page Cache 自动、透明、无需修改程序缓存不可控、可能被挤掉文件预加载(热加载)
  • tmpfsramfs 的主要区别
特性 tmpfsramfs
存储位置内存 + Swap 纯内存(永远不使用 Swap)
内存限制可限制大小(推荐)不可限制,体积可无限增长
系统内存压力大时会将部分数据 Swap 出去继续占用内存,最终导致系统 OOM
默认缓存行为文件内容存放在 Page Cache 中,这些页是可回收的,内核在内存压力大时可以回收或 Swap Out 文件内容同样存放在 Page Cache 中,但被标记为不可回收,不会 Swap Out,也不会被回收,因此会永久占用物理内存
OOM 风险低(可回收)极高(不可回收)
性能与 ramfs 速度差异极小,几乎相同比如 tmpfs 略快,但差距可以忽略
适用场景安全缓存、高性能临时存储特殊场景、嵌入式、需要可预测行为