3、缓存与缓冲调优:Page Cache 的核心参数
文件系统调优里,缓存和缓冲这块儿,我把它比作「系统的肾上腺素」。用好了,性能飞起;用不好,系统卡死甚至丢数据。今天咱们就聊聊 Page Cache 机制,以及那几个关键的 dirty 参数和 vfs_cache_pressure。
说白了,Linux 会把读过的文件数据留在内存里,这就是 Page Cache。下次再读同样的数据,直接从内存拿,不用去碰慢悠悠的磁盘。写操作也一样,先写到 Page Cache,标记成「脏页」,后台再慢慢刷到磁盘。这套机制让系统 I/O 看起来快得离谱。
但问题来了——内存是有限的。脏页太多,系统来不及刷盘,突然断电怎么办?脏页太少,磁盘写入频繁,性能又上不去。这就是我们需要调优 dirty_ratio、dirty_background_ratio 这些参数的原因。
核心思路:在内存压力和 I/O 吞吐之间找到平衡点。既要让 Page Cache 充分利用空闲内存,又要控制脏页比例,防止系统卡死或数据丢失。
3.1 Page Cache 机制:为什么它这么重要?
Page Cache 是 Linux 内核的「缓存层」。你读一个文件,内核先把数据从磁盘读到内存,下次再读就直接命中缓存。写操作也是先写到缓存,标记为脏页,然后异步刷盘。
我遇到过一台嵌入式设备,跑的是 NAND Flash,读小文件特别频繁。一开始没调 Page Cache,每次读文件都直接走磁盘,延迟高得离谱。后来把 Page Cache 调大,命中率从 30% 升到 85%,用户体验直接上了一个台阶。
但注意,Page Cache 不是越大越好。它占的是应用程序可用的内存。你想想看,如果系统总内存才 512MB,你把 400MB 都给了 Page Cache,应用程序还怎么跑?
3.2 dirty_ratio 与 dirty_background_ratio:脏页的「水位线」
这两个参数控制脏页占系统内存的百分比。它们就像两个水位开关:
- dirty_background_ratio:后台刷盘线程启动的阈值。默认 10%。脏页达到这个比例,内核唤醒后台线程开始刷盘。
- dirty_ratio:同步刷盘的「硬上限」。默认 20%。脏页达到这个比例,应用程序自己的写操作会被阻塞,直到脏页刷下去。
说白了,dirty_background_ratio 是「温柔提醒」,dirty_ratio 是「强制刹车」。我曾经在一个视频采集设备上遇到过问题:写入码流很大,dirty_ratio 默认 20%,结果脏页一冲到上限,写操作突然卡住,导致丢帧。后来我把 dirty_ratio 降到 10%,dirty_background_ratio 降到 5%,刷盘更积极了,卡顿问题就解决了。
| 参数 | 默认值 | 作用 | 调优建议 |
|---|---|---|---|
| dirty_background_ratio | 10% | 后台刷盘线程启动阈值 | 写频繁设备可降至 3-5% |
| dirty_ratio | 20% | 同步刷盘硬上限 | 写敏感场景可降至 8-10% |
注意:dirty_ratio 设置过低会导致频繁同步刷盘,增加 CPU 开销和磁盘磨损。不要为了追求「安全」而把值压得太低,尤其是 SSD 设备,频繁刷盘会缩短寿命。
3.3 dirty_expire_centisecs 与 dirty_writeback_centisecs:刷盘的「时间闸」
除了百分比,内核还给了两个时间参数来控制脏页刷盘行为:
- dirty_expire_centisecs:脏页的「过期时间」。默认 3000 厘秒(30 秒)。脏页在内存里待超过这个时间,就会被后台线程刷走。
- dirty_writeback_centisecs:后台刷盘线程的唤醒间隔。默认 500 厘秒(5 秒)。每隔 5 秒,内核检查一次是否有脏页需要刷。
这两个参数配合使用。dirty_writeback_centisecs 控制检查频率,dirty_expire_centisecs 控制脏页的最大存活时间。
我调过一台工控机,跑的是实时控制程序。默认 30 秒的过期时间太长,一旦断电,30 秒内的数据全丢。我把 dirty_expire_centisecs 降到 500(5 秒),dirty_writeback_centisecs 降到 100(1 秒)。代价是磁盘写入频繁了一些,但数据安全性大幅提升。
我的习惯:对于电池供电的嵌入式设备,我会把 dirty_expire_centisecs 设到 1000-2000(10-20 秒),兼顾功耗和数据安全。对于插电设备,可以放宽到 3000-5000。
3.4 vfs_cache_pressure:VFS 缓存回收的「油门」
这个参数控制内核回收 dentry 和 inode 缓存的「积极性」。默认 100,表示内核按正常比例回收。值越大,回收越积极;值越小,内核越倾向于保留这些缓存。
你想想看,dentry 和 inode 缓存是用来加速文件路径查找的。如果系统频繁创建和删除文件(比如日志系统、临时文件目录),这些缓存会膨胀得很快。vfs_cache_pressure 设高了,缓存回收快,但文件操作可能变慢;设低了,缓存占用内存多,但文件路径解析快。
我曾经在一个嵌入式路由器上遇到过问题:频繁读写 /tmp 下的临时文件,dentry 缓存涨到 200MB,系统内存只剩 50MB 可用。我把 vfs_cache_pressure 从 100 调到 200,缓存回收更积极了,内存压力缓解了,文件操作速度也没明显下降。
| vfs_cache_pressure 值 | 行为 | 适用场景 |
|---|---|---|
| 0 | 内核几乎不回收 VFS 缓存 | 内存充足、文件操作极频繁 |
| 50 | 回收较保守 | 通用嵌入式设备 |
| 100(默认) | 正常回收 | 大多数场景 |
| 200 | 回收积极 | 内存紧张、临时文件多 |
避坑指南:不要把 vfs_cache_pressure 设成 0。我曾经试过,结果系统内存被 dentry 缓存吃光,OOM Killer 把关键进程杀了。除非你非常清楚自己在做什么,否则建议在 50-200 之间调整。
3.5 实战调优:一个完整的配置示例
下面是我在一台 512MB 内存的嵌入式设备上用的配置。设备跑的是视频流写入 + 日志记录,对数据安全要求较高:
# /etc/sysctl.d/99-cache-tuning.conf
# 脏页比例调低,减少数据丢失风险
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
# 脏页过期时间缩短,刷盘更积极
vm.dirty_expire_centisecs = 1000
vm.dirty_writeback_centisecs = 200
# VFS 缓存回收适度积极
vm.vfs_cache_pressure = 150
应用配置:
sysctl -p /etc/sysctl.d/99-cache-tuning.conf
调完之后,用 cat /proc/meminfo 观察 Dirty 和 Writeback 字段,确认脏页比例在预期范围内。我习惯跑一周的负载测试,看看有没有内存压力或者 I/O 卡顿。
总结一下:缓存与缓冲调优,本质是「内存换性能」和「安全换速度」的权衡。没有万能参数,只有适合你场景的参数。我的建议是——先理解你的 I/O 模式,再动手调,调完一定要验证。