最后更新: 2025-12-31, 作者: 官方小编
问题背景
近期,公司某机房的一些机器出现偶发概率性的服务毛刺,具体表现如下:
多业务线均反馈了类似问题,除了业务毛刺,还会偶发的cpu.idle掉底毛刺,整体影响面比较大。

影响范围
据统计约5个集群和业务线。
问题分析
首先观察HUATUO故障定位大盘,看看毛刺时间点有什么特别的事件发生。如下所示,监控大盘显示有内存回收发生,回收量曲线直接出现90度上升。

在一些毛刺特别严重的时刻,我们在HUATUO抓取的CPU火焰图调用栈如下:

从上图的火焰图可以很清晰的看到,有无数个栈帧都在做 shrink_xxx,这非常明显的指出系统是在做内存直接回收。对多个不同业务的多个毛刺事件进行观测,结论均为内存回收。
接下来需要搞明白,为什么会出现整机的内存回收。业务同学提供了多个出问题的容器。继续观察发现出问题的机器和原来一直没有问题的机器的机型不同。我们分别对比这些机器的内存直接回收和kswapd曲线。
有问题机器:

从图中可以看到直接回收出现的相当频繁,数量级也是相当的大,甚至能达到1秒回收50G的程度,下图的cpu.sys的趋势与直接回收大致相同,最坏情况甚至能够达到80%。

再来看下无问题机器的监控,虽然也有直接回收,但是数量级非常小,而且与cpu.sys也没有明显相关性

两种机器的水位线配置是完全一样的,实际算出来的水位线 min=4G,low=5G,high=6G。
vm.min_free_kbytes=4G
vm.watermark_scale_factor=10
- 有问题机器为单路服务,内存为750GB,一个 kswapd 线程负责异步回收这些内存。
- 无问题机器为双路服务,总内存500GB,每node 250GB,两个 kswapd 线程异步回收这些内存。这样一来,在内存短缺时,两者的回收速度是截然不同的,所以无问题服务器能够通过 kswapd 的不断活跃来避免直接回收,但是经常出问题的服务器的一个 kswapd 有时就来不及回收到足够的内存,应用程序不得不进入直接回收,从而造成一个个大毛刺。
内核原理
Linux 内核的内存分为匿名页和文件页,匿名页是无法回收的(swapoff),文件页是可以回收的。所以遇到内存短缺时内核会尝试回收部分文件页内存。
回收的手段有两种,一种是kswapd异步线程,此方法是异步进行的,不影响其他业务程序运行。另一种是直接回收,所谓直接,就是同步回收,这个过程发生于业务进程上下文里,会直接阻塞业务运行。

Linux 内核的内存管理有三条水位线。分别是min, low, high。 当 free 内存低于 low 时,会先启动 kswapd 异步线程做回收,回收的目标是让 free 内存回到 high 以上。如果业务进程内存使用非常剧烈,分配速度比 kswapd 的回收速度还要快,那么就会击穿 min 和 low 之间的内存量,当 free 内存降低到 min 以下时,所有试图分配内存的进程,都将在进程上下文进行同步的直接回收,这会导致严重的业务毛刺。 我们本文的策略就是保证 min 水位不变,提高 low 和 high 水位,让 kswapd 尽早起来干活,拉大 low 和 min 的间距可以提供足够的缓冲区。
总结
此问题的根本原因是当机型内存很大,如750G,且是单node机器,只有一个kswapd线程,kswapd 负责的内存太多,导致工作效率跟不上内存分配速度,所以部分业务进程会在内存短缺时进入内存直接回收,进而导致阻塞和毛刺。
解决方案
解决此问题需要让 kswapd 尽早起来工作,来弥补其单 kswapd 的劣势。经过验证,我们发现将 vm.watermark_scale_factor 从10改为150时,对应的水位线会从现在的 min=4G,low=5G,high=6G,变为min=4G,low=14.3G, high=26.6G,此调整不改变 min 水位线,不影响直接回收的进入时机,仅调大了 low 和 min 的间距,让 kswapd 提前起来干活,策略上比较安全,可以为突发内存分配提供足够的缓冲。
关于 vm.watermark_scale_factor
- 该参数用于控制内存水位的间距,从而影响内核的内存回收行为,主要是 kswapd 守护进程的唤醒和睡眠时机。
- 该参数不改变 min 水位,只影响 min 水位到 low 和 high 的间距。
- 默认值为 10(对应总内存的 0.1%),取值范围为 0-3000(即 0% 到 30%)。
WMARK_LOW = WMARK_MIN + (zone->managed_pages * vm_watermark_scale_factor) / 10000
WMARK_HIGH = WMARK_LOW + (zone->managed_pages * vm_watermark_scale_factor) / 10000
vm.watermark_scale_factor=150
实际效果
目前试点的机器自从参数调整后,再也没出现过直接回收的问题

篇尾:
- 关注微信公众号【HUATUO 开源技术】留言,或扫码添加工作人员微信,邀请您加入用户群(请备注姓名+单位):

- 代码仓库:https://github.com/ccfos/huatuo or https://gitlink.org.cn/ccfos/huatuo
- 官方网站:https://huatuo.tech/