干运维这些年,最让人抓狂的故障从来不是"服务挂了",而是"服务没挂,但就是不干活"。业务日志里没有一行报错,进程还在,CPU 也不高,内存还剩一大截,可写文件的那个线程就是停在那儿几十毫秒甚至几秒钟不动。你去看监控,一切正常;你去问开发,开发说"我就是调了个 write()"。这类问题十有八九跟内核的脏页回写有关,而它偏偏是很多人从来不看的一层。写文件写到一半突然卡住,不是磁盘突然坏了,多数时候是脏页比例撞上了那条强制回写线,内核开始逼着写的人自己把活干完。
先把话挑明,这篇要讲的不是文件系统怎么选,也不是 SSD 的垃圾回收怎么回收,那些是另一个话题。这篇只讲一件事:从 write() 返回成功,到数据真正落到块设备上,中间隔着的那层页缓存、那几个 sysctl 阈值,以及它们联合起来怎么把一次普通的写变成一次肉眼可见的停顿。下面的几条结论,是我排查这类问题时真正会用的判断顺序:
一、write() 返回成功只代表数据进了页缓存,不代表落盘。此时被改过的内存页叫脏页,它迟早要被写回块设备,但"迟早"这两个字就是停顿的来源。
二、"突然卡住"的直接原因,多半是脏页比例撞上了 dirty_ratio 那条线。到这条线之后,产生新脏页的进程会被内核强制同步回写,也就是被 balance_dirty_pages 拦在原地,写多快取决于盘有多快。
三、大内存机器上按百分比算脏页是个著名陷阱。几百 GB 内存的机器,默认百分比换算出来是几十 GB 的脏页,一次集中回写足够把普通磁盘打满几秒到几十秒。
四、脏页高不等于故障,但"高且长时间不下来"一定是信号。停顿的严重程度取决于盘能吞多快,取决于阵列卡缓存有没有掉电保护,也取决于这台机器到底是物理机还是云主机。
五、容器里调 vm.dirty_* 通常无效,也不该调。页缓存和这几个 sysctl 都是宿主机级别的全局参数,容器只是共享者,不是拥有者。
应用程序调用 write(),绝大多数情况下写的是页缓存(page cache)——内核在内存里给文件数据开的一块缓冲。写进去之后,对应的内存页内容跟磁盘上的内容不一致了,这种页就叫脏页(dirty page)。内核会在之后某个时刻把脏页真正写到块设备上,这个动作叫回写(writeback)。负责回写的是内核的后台回写线程:历史上叫 pdflush,2.6.32 之后改成了 per-backing-device 的 flusher 线程,也就是每个后备设备有自己的回写线程,不再是一组全局线程抢着干。
这么设计是有道理的。内存比磁盘快几个数量级,如果每次 write() 都等磁盘转完、等磁头到位、等 NAND 写完再返回,那任何程序都会慢到没法用。页缓存把"写"这个动作变成了内存操作,代价是数据有一段时间只存在于内存里,掉电就没。这是内核做的一笔明确的交易:用一致性风险换性能,然后用一堆阈值参数来控制这个风险敞口有多大。
write() 的返回值只承诺一件事:数据被内核接走了,进了页缓存,后续同一进程读这个文件能读到刚写进去的内容。它不承诺数据在磁盘上。这一点在本质上是"缓冲写"的定义,不是 bug。麻烦在于,很多写代码的同学把 write() 当成"落盘"来用,写完就认为数据保险了,于是进程被 kill、机器重启、宿主机掉电之后,东西没了才来问"我明明写成功了"。
内核之所以敢这么干,是因为它相信自己迟早会把脏页写回去;程序之所以会卡,是因为"迟早"这三个字的调度权不在程序手里。脏页在内存里攒着的时候,写入方感觉飞快;一旦攒到了阈值,内核就要么让后台线程悄悄写,要么按住写入方让它自己写。前者你感觉不到,后者你就卡住了。
fsync()(以及 fdatasync()、msync() 这一类)就是显式地要求内核:把这个文件相关的脏页现在、立刻写回块设备,并且等写完再返回。它是应用手里唯一一个能主动触发并等待回写的开关。说白了,write() 是"交给内核了",fsync() 是"我现在就要确认它在盘上了"。
这里有个很容易被误解的点:fsync 慢,不一定是盘有问题,很可能是盘上正压着一大堆之前攒下的脏页,你的 fsync 被迫排在队尾。所以 fsync 的耗时其实是"整个回写队列的健康度"的一个探针,它变长,往往意味着之前有很长时间的写入没有及时沉降下去。数据库类程序对 fsync 延迟极其敏感,原因就在这里——它们的正确性依赖 fsync 真的返回,而它们的性能又依赖 fsync 不要太慢。
控制脏页行为的那几个 sysctl 都在 /proc/sys/vm/ 下面,名字长得像,作用差别很大。搞反 background 和 ratio 是新手最常见的错误,把 bytes 和 ratio 一起设是第二个常见错误。
dirty_background_ratio / dirty_background_bytes:脏页的占比或字节数达到这个阈值,内核唤醒后台回写线程开始异步回写。注意是异步——回写在后台发生,写入方继续写,不阻塞。这条线是"该动手了"的提示,不是刹车。
dirty_ratio / dirty_bytes:脏页达到这个阈值,产生新脏页的进程会被强制同步回写,内核走的是 balance_dirty_pages 这条路径,进程被拦在那里,一边等回写一边才能继续写。这条线是刹车,而且是踩在写入方的脚上。这就是"写文件突然卡住"的直接原因,记住这一点,本文后面所有的排查都围着它转。
两条线的分工很清楚:background 那条线负责"平时保持盘上有事在做",ratio 那条线负责"实在攒太多了,谁也别再往里倒"。理想状态下,后台回写的速度能跟得上产生的速度,脏页永远碰不到第二条线;一旦后台跟不上——盘太慢、写入太猛、阵列卡缓存突然写满——脏页就会越过 background 一路涨到 ratio,这时候业务侧的写延迟就会出现台阶式抬升。
dirty_expire_centisecs:脏页存活多久之后,就会被回写线程考虑写回。单位是百分之一秒,也就是厘秒,不是毫秒,这一点看名字容易记错。它管的是"页有多老",防止一块脏页因为总量一直没到阈值而在内存里躺一辈子。
dirty_writeback_centisecs:回写线程多久醒一次。同样是百分之一秒为单位。醒得越勤,脏页被清理得越及时,代价是唤醒本身和回写带来的额外开销;醒得太懒,脏页就容易攒起来。这两个时间参数跟比例参数是两套维度:一个管"什么时候该考虑写",一个管"攒到多少必须写"。只调一边,另一边照样能把你卡住。
ratio 与 bytes 是互斥的两套写法:给其中一个写非零值,另一个会被清零。也就是说,你设了 dirty_background_bytes,dirty_background_ratio 就变成 0,反之亦然。很多人写调优脚本的时候把四个参数全写进去,结果后写的一半把先写的一半清零了,自己还以为都生效了,调完发现行为完全没变,或者变得莫名其妙。改完一定要回读一遍 /proc/sys/vm/ 下对应的值,确认到底哪套在起作用。
这是我最想让人记住的一段。比例参数在小内存年代是很合理的:一台 8GB 内存的机器,百分之十就是八百 MB 左右,攒到这么多脏页开始强制回写,盘上一两秒就消化完了,人几乎感觉不到。可内存涨到几百 GB 之后,同一套百分比的含义完全变了。
一台几百 GB 内存的机器,vm.dirty_ratio 默认是百分比形式,10% 或 20% 换算出来就是几十 GB 的脏页。几十 GB 是什么概念?普通磁盘哪怕顺序写,把这几十 GB 集中回写完也要几秒到几十秒。这几十秒里,任何一个正在产生新脏页的写进程都会被 balance_dirty_pages 拦住,表现就是"接口超时""上传卡住""日志突然断了一截"。而且它不是均匀分布的小卡顿,是一次性的大停顿——因为攒得太多了。这就是大内存机器常常改用 dirty_background_bytes / dirty_bytes 设绝对字节数的动机:把"可以攒多少"锚定在盘的处理能力上,而不是锚定在内存大小上。内存翻倍,允许攒的脏页量不该跟着翻倍,盘又没变快。
改成字节数,本质上是把控制权从"内存比例"手里拿回来,交给"磁盘能承受多少"这个真正相关的变量。你可以根据盘的实际吞吐算一个:希望在几秒内把峰值脏页排空,就按吞吐乘以秒数来定上限。代价是这个数字变成了跟硬件绑定的手工值——换盘、换阵列、换云盘类型之后,它就不一定合适了,得重新算。而百分比的好处是它自适应,坏处是它在内存越来越大的今天,自适应到了一个荒谬的量级。这是个取舍,不是谁绝对更好。
下面这张表把几个关键参数摆在一起,方便你对照着看自己在调什么:
| 参数 | 作用 | 触发时机 | 是否阻塞应用 | 常见误区 | 备注 |
|---|---|---|---|---|---|
| dirty_background_ratio / dirty_background_bytes | 唤醒后台回写线程,开始异步清理脏页 | 脏页占比或字节数达到该阈值 | 否,异步回写,写入方继续写 | 以为设了它就不会卡;它只是"该动手了",不是刹车 | ratio 与 bytes 互斥,写一个会清零另一个 |
| dirty_ratio / dirty_bytes | 触发 balance_dirty_pages,强制同步回写 | 脏页继续上涨到该阈值 | 是,产生新脏页的进程被拦住 | 一味调大想"少回写",结果把小停顿攒成大停顿 | "写文件突然卡住"的直接来源 |
| dirty_expire_centisecs | 限定脏页存活多久后应被考虑写回 | 单个脏页"年龄"超过该时间 | 否,由回写线程执行 | 把厘秒当毫秒算,数量级直接差十倍 | 单位是百分之一秒 |
| dirty_writeback_centisecs | 回写线程的唤醒间隔 | 每隔该时间醒一次检查 | 否,但过频会增加额外开销 | 只调比例不调唤醒间隔,老脏页照样躺着 | 单位同样是百分之一秒 |
| ratio 与 bytes 的关系 | 同一目标的两种计量方式 | 写入非零值时另一个被清零 | 不直接阻塞,但误设会导致阈值失控 | 脚本里四个参数全写一遍,后写的一半把先写的清零 | 改完必须回读确认哪套生效 |
| 大内存上的百分比换算 | 把比例换算成实际允许攒的脏页字节数 | 内存越大,同样比例对应的绝对值越大 | 是,一次集中回写可打满盘几秒到几十秒 | 以为默认值在哪台机器上都是一样的效果 | 几百 GB 内存上 10% 或 20% 即几十 GB |
同样是脏页惹的祸,在不同场景里长得完全不一样。认出这三种长相,比背参数有用得多。
第一种:单次 write 延迟突然拉长。进程在产生新脏页的时候被 balance_dirty_pages 拦住,内核要求它先帮着把脏页写回去才能继续。表现是业务侧某个写操作平时几十微秒,此刻变成几十毫秒甚至几百毫秒,而且是成片出现——因为所有在写的人都一起被拦。这种卡顿跟"磁盘慢"的区别在于:它跟你的写入速率强相关,你写得越猛,越容易触发;停一会儿写,延迟就恢复了。
第二种:fsync 迟迟不返回。程序显式要求回写并等待,但队列前面压着一大堆之前攒下的脏页。数据库、消息队列、日志落盘这类依赖 fsync 保正确性的程序,在这里会表现得特别明显:事务提交时间变长、刷盘线程掉队、 checkpoint 越拖越久。fsync 慢是结果,不是原因,锅常常在"之前太久没有及时回写"。
第三种:整个实例的 IO 等待把业务线程一起拖住。回写把块设备队列填满之后,不只是写的人慢,读的人也一起慢——读请求要排在写请求后面等队列。这时候你看 CPU 的 iowait 会明显抬升,业务线程池逐渐被占满,接口大面积超时,但没有任何一个组件"报错"。这种最容易被误判成"是不是网络出问题了""是不是上游限流了",其实盘上正堵着。
排查这类问题不需要什么高级工具,内核自己就把数据摆出来了,关键是知道每个入口能证明什么、不能证明什么。
/proc/vmstat 里的 nr_dirty 与 nr_writeback。这是最直接的两个数:nr_dirty 是当前脏页数量,nr_writeback 是正在回写中的页数量。nr_dirty 高说明攒着没写下去,nr_writeback 高说明正在写但写得吃力。两个一起看,能区分"还没开始写"和"正在写但写不动"。这个适合做成每秒采样的曲线,比抓一次快照有用得多——你要看的是形状,不是某个瞬间的值。
cat /proc/meminfo 的 Dirty 与 Writeback 字段。这是同一批信息的另一种呈现,单位直观,适合人工快速看一眼,或者塞进现有的内存采集脚本里。很多监控只采 MemFree、Buffers、Cached 就把内存监控交差了,独独漏掉 Dirty,于是磁盘已经堵了,内存面板还是一片绿,这是最典型的漏看。
iostat -x 看 await 与 %util。await 是 IO 请求的平均等待时间,%util 是设备忙的比例。%util 长期接近打满,说明设备吞吐就是瓶颈,回写再怎么调度也快不了;await 突然拉长而 %util 还没满,往往是队列深度或者缓存行为出了问题,比如阵列卡缓存刚写满。这两个数合起来能回答"到底是盘不行,还是调度不行"。
sar -r 与 sar -B。sar -r 看内存整体使用情况,sar -B 看换页统计(pgpgin/pgpgout/pgfault)。pgpgin/pgpgout 反映的是换入换出的量,配合内存和脏页曲线,能帮你判断这台机器的内存压力是不是已经影响到了页缓存的正常工作。sar 的价值在于它有历史——故障发生的时候你多半不在屏幕前,事后能翻的只有它。
为什么同样一套 dirty 参数,在一台机器上平平稳稳,在另一台机器上就能卡出几十秒?答案往往不在参数本身,而在参数底下的那层硬件把延迟藏起来了。
RAID 卡自带的缓存(带 BBU 电池或者超级电容保护时)会先把写接下来,立刻告诉内核"写完了",再慢慢往后面的磁盘上落。因为有掉电保护,它敢这么做——掉电了缓存里的东西靠电池或电容撑着,下次上电还能写回去。这个机制让写延迟在前半段非常平,脏页回写看起来又快又稳。问题在于,缓存的容量是有限的,写入速度超过缓存往后端盘沉降的速度,缓存就会被填满。填满之后,延迟不是平滑上升,是陡增——前面有多平,后面就有多陡。这条曲线和脏页回写叠加起来,会把停顿放大:内核看到前面写得很快,于是继续攒脏页,等阵列卡缓存一满,两头的压力同时落到盘上,停顿就变成了几十秒量级。
这也是为什么"带不带掉电保护"在数据库类负载上是硬指标。没有保护的缓存要么不敢开写缓存,写了快不起来;要么开了写缓存但掉电就丢数据。这两种我都见过,也都很难收场。
云主机和虚拟化场景更复杂一点:guest 内部的回写,还要再穿过一层宿主机和后端存储。guest 里 fsync 返回了,只能说明宿主机这一层接住了,离数据真正在后端存储上持久化还隔着一层。带电池保护的阵列卡跟云盘的行为完全不同,物理机上那套"阵列卡帮我扛一会儿"的经验不能照搬到云盘上——云盘的延迟特征、吞吐上限、多租户下的抖动,都是另一套。
更要紧的是,guest 内部的回写被宿主机再调度一次,意味着你看到的性能曲线是两层行为的叠加,排查的时候很容易误判:guest 里 iostat 显示盘没满,实际上瓶颈在宿主机那一层,guest 根本看不见。所以云主机上遇到写停顿,光看 guest 内部的指标是不够的,得看云平台给你的那个层面的监控,或者干脆找服务商确认后端存储当时的状态。
如果说哪类程序最容易被脏页回写坑,那一定是数据库。不是因为它写得慢,恰恰是因为它写得又快又讲究。
数据库为了保证崩溃后能恢复,普遍采用预写日志:改动数据之前先把日志写出来,日志要 fsync 落盘,事务才算提交。同时还有 checkpoint(把内存里改过的数据页成批刷回数据文件)和 doublewrite(先把页写到一个连续区域再写到真正位置,防止部分写)这类机制。这几样东西的共同点是:它们自身就在持续制造大量脏页,而且制造的速度往往超过盘能吸收的速度,尤其在写入高峰。
于是数据库和内核的脏页机制会互相较劲:数据库按自己的节奏刷页,内核按自己的阈值强制回写,两边的写请求在块设备队列上撞在一起。数据库的刷页有自己的节奏考虑——它希望平滑,希望别在高峰期集中刷;内核的强制回写不管这些,脏页到线了就得写。结果就是数据库精心设计的平滑刷页策略,被内核的一次集中回写打乱,表现在业务上就是周期性的延迟尖峰。
把 dirty_ratio 调大,能减少触发强制回写的次数,平时确实更顺;但代价是每次触发时要排空的脏页更多,小停顿攒成了大停顿,一次卡几十秒的概率反而上升。把 dirty_ratio 调小,回写更频繁、单次停顿更短,代价是回写总量上升、写放大增加,盘的压力更大,对 SSD 的寿命也不是好事。这就是取舍,不是越大越好,也不是越小越好。
真正合理的做法是先量:把 nr_dirty 做成曲线,看它在业务高峰能涨到多少、盘排空它需要多久,再倒推一个能接受的阈值。脱离实测数字去抄别人的"调优参数",基本等于赌。数据库类负载挑机器,我更看重能不能拿到带掉电保护的阵列卡和说得清回写行为的盘——一万网络深耕 19 年(成立于 2007 年),这类细节工单里问得到答案,比看宣传页实在。
容器场景有个很常见的误解:以为进了容器就拥有一台"小机器",什么都能自己调。实际上容器共享的是宿主机的内核,页缓存是共享的,vm.dirty_* 这些 sysctl 也是全局的,不是 per-container 的。在容器里调 vm.dirty_* 通常无效,也不该调——它是宿主机级别的参数,改了会影响同宿主机上所有容器,而且大多数容器运行时压根不会给你这个权限,写了也是白写,或者根本写不进去还报错。
正确的做法是:容器里别碰这些参数,把需求提给管宿主机的人。如果一台宿主机上跑着写密集型的数据库容器和一堆读多写少的 Web 容器,那么宿主机上的 dirty 参数必须按最挑剔的那个来定,因为它是全局的,没法给某一个容器单独开小灶。这也是为什么很多团队最后选择把数据库放到独立的物理机或者独立宿主机上——不是性能不够,是全局参数没法隔离。
云主机好一点:guest 里的 sysctl 通常能改,也确实在 guest 内生效。但 guest 内生效不等于端到端生效,底下还有宿主机和后端存储两层。所以云主机上调完参数,一定要用真实的写入压测去验证,别只看 guest 里的指标变好看了就收工。
第一条:监控里没有 Dirty 这一项。坑在于内存面板只看 used/free,脏页涨到几十 GB 也照样显示"充足",等 IO 停顿发生了,所有面板都是绿的,没人知道该看哪儿。怎么避:把 /proc/vmstat 的 nr_dirty 和 nr_writeback 纳进常规采集,做成每秒曲线,跟 iostat 的 await 放在同一张图上对照看。
第二条:大内存机器照抄百分比默认值。坑在于同一套百分比在 16GB 内存和 512GB 内存上代表的实际脏页量差了几十倍,内存升级之后问题才暴露,而且暴露的方式是一次性大停顿,很难联想到是内存变大了。怎么避:内存上百 GB 的机器,认真考虑改用 dirty_background_bytes / dirty_bytes 设绝对字节数,按盘的吞吐乘上你能接受的排空时间来算。
第三条:把 dirty_ratio 一味调大,以为能"少回写"。坑在于减少的是次数,增加的是每次的量,小停顿攒成了大停顿,还可能跟阵列卡缓存写满的那一刻撞在一起。怎么避:调大之前先测一次排空耗时,确认最坏情况下的停顿你能接受;同时盯住停顿的最大值和发生频率,别只看平均值。
第四条:ratio 和 bytes 一起设。坑在于写一个非零值会把另一个清零,脚本里四个参数全写一遍,结果后写的一半把先写的清零了,实际生效的跟你以为的完全是两套。怎么避:一次只用一套,改完立刻回读 /proc/sys/vm/ 下对应的四个值,确认到底哪套生效、另一个是不是真的被清零了。
第五条:在容器里调,或者没 fsync 就以为数据安全。坑在于前者根本不生效还浪费排查时间,后者把"写成功"当成"已持久化",掉电或强杀之后才发现东西没了。怎么避:容器里的参数需求提到宿主机层面解决;涉及重要数据的写入,该 fsync 的地方老老实实 fsync,并且把 fsync 的耗时纳入监控——它变长就是回写在报警。
不一定。脏页存在的意义就是让写入变快,业务在写的时候 nr_dirty 往上走是正常的,能在后台线程一轮回写里降回去,那就什么事都没有。真正要警惕的是形状:脏页长期高位横着不下来,说明回写速度跟不上产生速度;或者脏页一路涨到强制回写线附近反复横跳,说明每次都在触发同步回写。这时候即使业务还没报慢,也已经站在停顿边缘了。判断标准从来不是绝对值,而是"涨上去之后能不能及时下来"。
会,但伤的方式和你想的可能不一样。调小之后回写触发得更频繁,单次写回的量变小,停顿变短,这是好处。代价是回写变得更碎、更频繁,总的写入放大增加,机械盘上寻道次数变多,SSD 上则意味着更多的写入量。所以调小换来的"短停顿"不是免费的,它用盘的寿命和整体吞吐去换。合理做法是算一笔账:你能接受的最长停顿是多少秒,盘的吞吐是多少,两者相乘就是你允许的峰值脏页量,再往上加一点余量。
因为默认情况下允许攒的脏页是按内存比例算的,内存翻倍,允许攒的脏页就翻倍,可是磁盘的吞吐没有跟着翻倍。于是内存越大的机器,攒出来的那一波脏页越夸张,一次集中回写要花的时间越长。几百 GB 内存的机器上,10% 或 20% 换算出来是几十 GB,一次回写打满普通磁盘几秒到几十秒是很正常的事。这就是大内存机器改用绝对字节数的全部理由:让阈值跟着盘走,别跟着内存走。
基本不是 fsync 自己的锅。fsync 只是提出要求并等待,它等的是前面排在队列里的所有回写完成。如果之前有很长时间脏页没有及时沉降,你的 fsync 就会排在长长的队尾,耗时自然就上去了。所以看到 fsync 慢,第一反应应该是去看 nr_dirty 和磁盘队列,而不是怀疑调用本身。反过来讲,fsync 的耗时是一个非常好用的健康指标——它变长,通常意味着回写链路已经在积压了。
不建议,也通常调不动。容器共享宿主机的内核、页缓存和这些全局 sysctl,vm.dirty_* 是宿主机级别的参数,不是容器级别的。多数容器运行时也不允许在容器里写这些值,写了要么报权限错误,要么根本不生效。真有需求,正确路径是在宿主机层面统一调整,并且记住它会影响这台宿主机上的所有容器。如果某个容器确实需要跟别人不一样的回写策略,那更合理的做法是把挪到独立的宿主机上。
guest 里的 fsync 返回只能证明宿主机这一层接住了,不能证明后端存储写完了,中间还隔着宿主机和后端存储两层。要验证,得从两个方向入手:一是问清楚服务商这块云盘的持久化语义,fsync 在哪一层被认为是持久的,有没有类似写缓存的机制以及掉电保护;二是自己做一次真实压测,写入后用断电、强制重启或者重建实例的方式去检验数据完整性,别只相信 guest 里的指标。带电池保护的阵列卡和云盘的行为完全不同,物理机经验不能照搬。
遇到写停顿,第一手资料永远是内核自带的文档——你所用发行版和内核版本对应的 sysctl 文档里,把每个参数的含义、单位、默认值写得清清楚楚。别去抄博客里的"万能调优值",默认值本身就以所用发行版与内核版本为准,不同版本之间是有差异的,抄错了比不调还麻烦。
第二件事是确认你有没有改的权限。物理机或者独立宿主机上,你有 root 就能改;容器里基本改不动;云主机的 guest 里通常能改,但要明白改的只是 guest 那一层。权限和作用域搞混,是很多人"调了没效果"的真正原因。
第三件事是找服务商确认硬件这一层:阵列卡有没有缓存、有没有 BBU 或者超级电容保护、缓存多大、写满之后的行为是什么;云主机的话就问后端存储的类型和持久化语义。这些信息在宣传页上基本找不到,但只要服务商真的自己运维机房,工单里是问得到的——一万网络的 7×24 中文工单平均 5 分钟响应,硬件故障 10 分钟自动迁移,深圳南山总部自营机柜最快 1 分钟上架,这类细节适合在下单前就把话说清楚,别等出了停顿再来猜。
改参数只是第一步,验证才是重点。我一般的排法是三件事连着做:先看回写曲线,把 nr_dirty 和 nr_writeback 做成每秒采样,跑一轮业务高峰或者压测,看脏页涨到多少、多久能降回去、有没有触到强制回写线,曲线的形状比任何单个数字都有说服力。
然后做停顿复测。光看平均值没意义,停顿是以最大值和分布的形式伤业务的。要把业务侧写延迟的 P99、P999 拉出来跟改之前对比,特别关注最坏那一次是多少——你能不能接受,取决于业务超时设在哪儿。改完参数之后如果 P99 降了但最大值没降,说明你只是把大停顿挪了个位置,没解决。
最后是把观测常态化。回写行为会随业务量、数据量、盘的状态慢慢变化,今天合适的阈值半年后未必合适。把 Dirty、Writeback、iostat 的 await 与 %util、sar -B 的换页统计放进长期面板并设上告警,比事后救火有用得多。回写是一条一直在流动的曲线,你只是在某个时刻给它设了个堤坝,水位涨上来还得再看一眼。
数据来源:文中为通用内核行为与常见取值逻辑,实际以所用发行版、内核版本与硬件/云盘后端文档为准;涉及具体默认值一律以所用发行版与内核版本为准。相关产品与服务能力以一万网络官网(https://www.idc10000.net/)公示为准,具体以签约时最新报价与合同为准。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品