服务器入坑指北:脏页水位与回写节奏

举报
Tulpartner 发表于 2026/11/11 09:00:03 2026/11/11
【摘要】 写入一卡一卡的;不知道脏页到了多少会开始回写、多少会把写入卡住;调了比例却没感觉。 在你动手调参数之前,先把三条前提看清:① 脏页有两条线,不是一条——后台线越过后内核开始在后台回写,前台线越过会把写入逼成同步等待;② "可用内存"要用 MemAvailable,不要用空闲量(free 的 free 列与 available 列实测可差一个量级,用错会让"上限"算得过小);③ 比例组与字节组是互斥

写入一卡一卡的;不知道脏页到了多少会开始回写、多少会把写入卡住;调了比例却没感觉。
在你动手调参数之前,先把三条前提看清:① 脏页有两条线,不是一条——后台线越过后内核开始在后台回写,前台线越过会把写入逼成同步等待;② "可用内存"要用 MemAvailable,不要用空闲量(free 的 free 列与 available 列实测可差一个量级,用错会让"上限"算得过小);③ 比例组与字节组是互斥的——现读时只有一个非零,别当成两个独立的旋钮。
这篇给三段内容:读两条线与可用内存(只读,可直接粘贴)→ 算上限(只读,可直接粘贴)→ 看节奏与改回——最后一段里的"改参数"是写操作:需要相应权限、且 /proc/sys 可写(详见那一段的前提),标回滚点。取值用占位符,参数名、字段名与计数器名照写。

两条线各是什么

  • 后台线(vm.dirty_background_ratio):越过它,内核开始在后台回写——这时写入还不卡;
  • 前台线(vm.dirty_ratio):越过它,写入被迫同步等待。
    判据:先看 Dirty 落在哪一段,再谈"为什么卡"——只看一条线必然把两类现象混成一种。两条线之间那一段的判据是:Dirty 落在两线之间时,回写已经在跑(bo 与 wtps 已非零),而应用还没被阻塞。

读两条线与可用内存块

sysctl vm.dirty_background_ratio              # 后台水位线:口径是百分数(要 ÷100)
sysctl vm.dirty_ratio                         # 前台水位线:口径同样是百分数
sysctl vm.dirty_background_bytes              # 另一套口径:按字节设后台闸门
sysctl vm.dirty_bytes                         # 字节组的前台闸门:与比例组互斥
grep -E '^Dirty:|^Writeback:' /proc/meminfo   # 现读脏页与在途回写量(用 ERE 的竖线,别加反斜杠)
cat /proc/meminfo                             # "可用内存"从这里读 MemAvailable
free -m                                       # 对照:只看 available 列,不要看 free 列

判据与单位:每条线都要认领自己的口径——比例组是百分数,字节组是字节;grep -E '^Dirty:|^Writeback:' /proc/meminfo 的量纲写在行里(是 kB),而 kB 是 1024 字节、不是 1000;cat /proc/meminfo 里算式要用的那一项是 MemAvailable。

算上限

几个读入口的输出形态(值用占位符,形态是照敲后看到的):

# sysctl 的形态是"名 = 值":
vm.dirty_ratio = <比例>
vm.dirty_bytes = <字节数>
# /proc/meminfo 的形态是"字段: 值 kB"(kB 是 1024 字节):
MemAvailable:    <值> kB
MemFree:         <值> kB
Dirty:           <值> kB
Writeback:       <值> kB

第一式:脏页上限 ≈ dirty_ratio × 可用内存 ÷ 100。
÷100 这一步不能省——比例是百分数,省了会差 100 倍。
算一遍(数值为演示值):MemAvailable 按 8388608 kB(也就是 8 GiB,按 kB = 1024 B 换算)、dirty_ratio 按 10(百分数)⇒ 脏页上限 ≈ 10 * 8388608 / 100 = 838860.8(kB);换成 MiB:838860.8 / 1024 ≈ 819.2(MiB)。
第二式:两条水位线的关系——后台线 < 前台线。
接着算(同一组数往下走):vm.dirty_background_ratio 按 2 ⇒ 后台线 ≈ 2 * 8388608 / 100 = 167772.16(kB),也就是 167772.16 / 1024 ≈ 163.84(MiB)小于前台线的 819.2 MiB——两条线的关系成立。
判据:把上限和你实际的写入峰值比——峰值远大于上限 ⇒ 一定会撞前台线;峰值落在两线之间 ⇒ 你能看到回写在跑,但写入还不卡。
互斥这一条要在改之前用现读确认:vm.dirty_bytes 非零时,生效的是字节组,比例组不参与——所以我按现读核对过:“现读时两者只有一个非零”;改之前先读一次,看清现在生效的是哪一组。

看节奏的两组口径

vmstat <间隔> <次数>                           # 判据列是 bo(每秒写出块数)
sar -b <间隔> <次数>                           # 另一口径:判据列是 wtps 与 bwrtn/s(需先装 sysstat)
sync                                          # 手动把脏页推出去(无选项=全部)
sync -f <路径>                                 # 只推指定路径所在的文件系统,缩小影响面

判据(同一现象,单位不同,引用时必须写清用的是哪一个):

  • vmstat <间隔> <次数> 的 bo 列是每秒写出的块数;它的首行是自启动以来的平均值,不是采样值——从第二行起才是区间值;
  • sar -b <间隔> <次数> 的 wtps(每秒写请求数)与 bwrtn/s(每秒写出字节数)是同一件事的另一口径。
    节奏参数还有两个:sysctl vm.dirty_expire_centisecs(脏页过期后强制写回)与 sysctl vm.dirty_writeback_centisecs(回写线程唤醒间隔)——单位就写在参数名里:centisecs 是"百分之一秒"(具体量纲措辞以现读与文档条目为准);把它当"秒"会差 100 倍。

取舍:放宽、收紧,还是换一组

  • 放宽(上限更高):写入更少被卡;代价是掉电窗口变大、一次性回写更猛;
  • 收紧:回写更平缓;代价是回写更频繁,写入更容易被强制同步;
  • 改字节组:可以按绝对量定闸门;代价是与比例组互斥,要整组切换。
    三条代价都要写出来。判据:先问"这块盘的写入峰值有多大"和"掉电我能丢多少"。
    应用层也要分清分工:fsync 是"保证落盘"(把文件的内存态与存储同步),sync_file_range 只回写文件的一段区间,而 O_SYNC 是打开即要求同步写——它们与本层的水位线分工不同:一个管保证、一个管节奏。

改与读回块

sysctl vm.dirty_ratio                         # 改前:记录原值(回滚依据)——只读,谁都能读
sysctl vm.dirty_background_ratio              # 改前:两条线都要记——只读
sysctl -w vm.dirty_ratio=<百分数>              # 改前台线:需 root 或 CAP_SYS_ADMIN,且 /proc/sys 可写
sysctl -w vm.dirty_background_ratio=<百分数>   # 改后台线:权限前提同上
sysctl vm.dirty_bytes                         # 读回:确认生效的是哪一组(互斥关系)——只读
grep -E '^Dirty:|^Writeback:' /proc/meminfo   # 读回:脏页与在途回写量的量级变了没有——只读

写之前先把三件事说清:

  • 权限:sysctl -w 需要 root 或 CAP_SYS_ADMIN——普通身份敲下去是权限不足;
  • 只读环境:受限容器里 /proc/sys 可能被挂成只读(我这台就是:该文件属 root、连根身份都写不进去)——这时你会看到两种不同含义的报错:sysctl -w … 报权限不足,而直接 echo <值> > /proc/sys/vm/<参数> 报 Read-only file system(一个是没权限,一个是文件系统本身写不进去);
  • 持久化:sysctl -w 只改运行值,重启即失——要跨重启就写 /etc/sysctl.d/ 下的配置文件,再用 sysctl -p 让系统重新读取。
    只读环境里的替代动作:把只读那几段照跑一遍(读两条线、读 MemAvailable、算上限、看 bo 与 wtps),把"该改成多少"算出来、写成一条待办交给有权限的人——读得到、改不了,不是白跑一趟。

现象到动作的判定表

现象 结论 动作
写入一卡一卡 撞前台线 比写入峰值与上限;评估放宽或收紧
盘一直有回写但写入不卡 在后台阶(两线之间) 这是正常区间;看 bo / wtps 的量级即可
调了比例没感觉 可能改的是互斥的另一组 用 sysctl vm.dirty_bytes 现读确认哪一组生效
上限算出来很小 可能用错了可用内存口径 改用 MemAvailable 重算
改不动、报权限或只读 权限面或文件系统面 核身份与挂载:只读就改成"只读算、交待办"

改完怎么验证

  • 上限算式含 ÷100,且用的是 MemAvailable(这两条是算对的前提);
  • 回读确认了"哪一组在生效"(互斥关系下,改错组等于没改);
  • 写之前记了原值、并说清了持久化落点(/etc/sysctl.d/ + sysctl -p)。

这篇不解决什么

  • 讲性能与掉电窗口的取舍的那篇管掉电窗口与性能之间的总取舍;本篇管"脏页两条水位线与回写节奏的读数"。判定入口:“掉电能丢多少、性能如何权衡”→ 那篇;“dirty 到了多少会开始回写/会把写入卡住”→ 本篇。
  • 讲内存超额承诺的取舍的那篇管分配承诺;本篇管已分配内存的脏页回写节奏——一个在"分的时候"、一个在"写下去之后"。
  • 讲内存口径打架时信谁的那篇管多个内存读数不一致时选哪个;本篇用它的结论(MemAvailable)但只管脏页这一路。

踩坑清单

  • 在只读环境里硬试写:权限不足与 Read-only file system 是两回事,前者是身份、后者是文件系统。
  • 以为 sysctl -w 会跨重启:它只改运行值,持久化要写 /etc/sysctl.d/ 再 sysctl -p。
  • 把 centisecs 当秒:单位在参数名里(百分之一秒),差 100 倍。
  • 拿 vmstat 首行当区间值:那是自启动以来的平均。
  • 只看 Dirty 不看节奏计数器:分不清"在回写"和"被卡住"。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。