Linux内核技术3 Page Cache - Page Cache管理不当问题分析之High Load

550 阅读9分钟

常见的三个High Load的问题
1.直接内存回收引起的 load 飙高或者业务时延抖动
直接内存回收是在进程申请内存的过程中同步进行的回收,而这个回收过程可能会比较久,后续行为都被迫等待,这样就会造成很长时间的延迟和CPU 利用率的升高,最终引起 load 飙高。

DMA回收过程
在开始内存回收后,首先进行后台异步回收(下图中蓝色标记的地方),这不会引起进程的延迟;当后台异步回收跟不上进行内存申请的速度,就会开始同步阻塞回收,导致延迟(上图中红色和粉色标记的地方,这就是引起 load 高的地址)。

image.png

解决思路
我们可以适当增加vm.min_free_kbytes的值,及早地触发后台回收来避免应用程序进行直接内存回收。 增大该值并不是完全避免直接内存回收,而是尽量将直接内存回收行为控制在业务可以容忍的范围内。

centos 7  linux 3.10.0
[root@yz117 ~]# sysctl -a|grep min_free 
vm.min_free_kbytes = 67584

centos6 
vm.extra_free_kbytes = 67584

后台回收的原理
当内存水位低于 watermark low 时,就会唤醒 kswapd 进行后台回收,然后 kswapd 会一直回收到 watermark high。 image.png

Red Hat建议该值设置不应该超过总内存大小的5%,也有建议将其设置成1~3%(How to tune the Linux kernel for memory performance - Knowledge Base / Operating System - Aerospike Community Forum), 我们可以看到4G的阿里云服务器设置为68M,大约是总内存的1.6%, 我们可以适当增大,建议设置为总内存的1/32,比如128G的服务器我们可以设置成4G,这样既不造成较多的内存浪费,又能避免掉绝大多数的直接内存回收。

Tips: 该值的设置和总的物理内存并没有一个严格对应的关系,所以在调整该值之前,我们可以渐进式地增大该值,比如先调整为 1G,观察 sar -B 中 pgscand 是否还有不为 0 的情况;如果存在不为 0 的情况,继续增加到 2G,再次观察是否还有不为 0 的情况来决定是否增大,以此类推。

2.系统中脏页积压过多引起的 load 飙高
在直接回收过程中,如果存在较多脏页就可能涉及在回收过程中进行回写,这可能会造成非常大的延迟,而且因为这个过程本身是阻塞式的,所以又可能进一步导致系统中处于 D 状态的进程数增多,最终的表现就是系统的 load 值很高。

脏页导致Load过高的过程:

image.png

解决思路
控制好系统中积压的脏页数据。
我们需要控制好脏页数据的数量,脏页控制的少了可能会影响系统整体的效率,脏页控制的多了还是会触发问题,

sar -r 1 观察kbdirty的数量

[root@yz117 ~]# sar -r 1
Linux 4.18.0-305.19.1.el8_4.x86_64 (yz117)      05/22/2022      _x86_64_        (2 CPU)

05:15:53 PM kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   **kbdirty**
05:15:54 PM    152416   2717636   3673928     96.02      8824   2666756   2730356     71.36    723864   2504436       552
05:15:55 PM    152416   2717636   3673928     96.02      8824   2666756   2730356     71.36    723864   2504440       552
05:15:56 PM    152416   2717636   3673928     96.02      8824   2666756   2730356     71.36    723864   2504440       552
05:15:57 PM    152416   2717636   3673928     96.02      8824   2666756   2730356     71.36    723864   2504440       552
05:15:58 PM    152416   2717636   3673928     96.02      8824   2666756   2730356     71.36    723864   2504484        88
05:15:59 PM    152416   2717636   3673928     96.02      8824   2666756   2730356     71.36    723864   2504484        88
05:16:00 PM    152448   2717668   3673896     96.02      8824   2666756   2730356     71.36    723864   2504484         0
05:16:01 PM    152448   2717668   3673896     96.02      8824   2666756   2730356     71.36    723864   2504484         0
05:16:02 PM    152448   2717668   3673896     96.02      8824   2666756   2730356     71.36    723864   2504484         0
^C
Average:       152427   2717647   3673917     96.02      8824   2666756   2730356     71.36    723864   2504464       265

dirty page相关参数
调大这些值会导致脏页的积压,但是同时也可能减少了 I/O 的次数,从而提升单次刷盘的效率;
调小这些值可以减少脏页的积压,但是同时也增加了 I/O 的次数,降低了 I/O 的效率。

vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 20

Tips: 至于这些值调整大多少比较合适,建议也是一边调整一边观察,将这些值调整到业务可以容忍的程度就可以了,即在调整后需要观察业务的服务质量 (SLA),要确保 SLA 在可接受范围内。

观察调整后的变化

[root@yz117 ~]# grep "nr_dirty_" /proc/vmstat
nr_dirty_threshold 202803
nr_dirty_background_threshold 67546

3. NUMA 策略配置不当引起的 load 飙高。
有时候系统中还有一半左右的 free 内存,但还是频频触发 direct reclaim,导致业务抖动得比较厉害,可能是因为zone_reclaim_mode设置为1导致的。

zone_reclaim_modec参数
设置 zone_reclaim_mode 的目的是为了增加业务的 NUMA 亲和性,但是在实际生产环境中很少会有对 NUMA 特别敏感的业务,这也是为什么内核将该配置从默认配置 1 修改为了默认配置 0。

zone_reclaim_mode配置为 0 之后,就避免了在其他 node 有空闲内存时,不去使用这些空闲内存而是去回收当前 node 的 Page Cache。

解决思路
减少内存回收发生的可能性从而避免它引发的业务延迟,我们可以将zone_reclaim_mode设置为0。

zone_reclaim_mode:0

一点补充

两种内存组织形式
UMA
即统一内存访问,总线模型保证所有的内存访问是一致的,即每个处理器核心共享相同的内存地址空间

NUMA
随着CPU由单核往多核的方向转换,UMA这样的架构难免遇到问题,比如对总线的带宽带来挑战、访问同一块内存的冲突问题,为了解决这些问题NUMA应运而生。\

即非一致性内存访问,在该架构下不同的内存器件和CPU核心从属不同的 Node,每个 Node 都有自己的集成内存控制器(IMC,Integrated Memory Controller)。
在 Node 内部使用 IMC Bus 进行不同核心间的通信;不同的 Node 间通过QPI(Quick Path Interconnect)进行通信。

特点 通常CPU访问Local Memory速度最快,访问Remote Memory速度随远近变化。

numactl --hardware指令
我们可以使用numactl --hardware指令,查看不同node之间的距离(矩阵表示)

$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 24 25 26 27 28 29 30 31 32 33 34 35
node 0 size: 130950 MB
node 0 free: 108256 MB
node 1 cpus: 12 13 14 15 16 17 18 19 20 21 22 23 36 37 38 39 40 41 42 43 44 45
node 1 size: 131072 MB
node 1 free: 122995 MB
node distances:
node 0 1 
 0: 10 21 
 1: 21 10 

image.png

内存管理的三个层次
内存管理从三个层次管理内存,分别是node, zone ,page;
64位的x86物理机内存从高地址到低地址分为: Normal DMA32 DMA.随着地址降低。

[root@yz117 ~]# cat /proc/zoneinfo  |grep "Node"
Node 0, zone      DMA
Node 0, zone    DMA32
Node 0, zone   Normal
Node 0, zone  Movable
Node 0, zone   Device

通过如下指令我们可以挂查到每个node都有自己的min, low, high zone,其单位为page。

[root@yz117 ~]# cat /proc/zoneinfo  |grep "Node 0, zone" -A10
Node 0, zone      DMA
  per-node stats
      nr_inactive_anon 138482
      nr_active_anon 273
      nr_inactive_file 487881
      nr_active_file 180692
      nr_unevictable 0
      nr_slab_reclaimable 44903
      nr_slab_unreclaimable 31936
      nr_isolated_anon 0
      nr_isolated_file 0
--
Node 0, zone    DMA32
  pages free     29244
        min      12650
        low      15812
        high     18974
        spanned  1044480
        present  782304
        managed  716768
        protection: (0, 0, 914, 914, 914)
      nr_free_pages 29244
      nr_zone_inactive_anon 82464
--
Node 0, zone   Normal
  pages free     5209
        min      4176
        low      5220
        high     6264
        spanned  262144
        present  262144
        managed  235978
        protection: (0, 0, 0, 0, 0)
      nr_free_pages 5209
      nr_zone_inactive_anon 56018
--
Node 0, zone  Movable
  pages free     0
        min      0
        low      0
        high     0
        spanned  0
        present  0
        managed  0
        protection: (0, 0, 0, 0, 0)
Node 0, zone   Device
  pages free     0
        min      0
        low      0
        high     0
        spanned  0
        present  0
        managed  0
        protection: (0, 0, 0, 0, 0)

高低水位
min,low,high可以看成是内存管理的高低水位,他们之间的计算公式如下:
low = 5/4 * min
high = 3/2 * min

内存回收的两种方式
Drect reclaim :
min下的内存是保留给内核使用的,当到达min,会触发内存的direct reclaim。
Kwapd reclaim :
low水位比min高一些,当内存可用量小于low的时候,会触发kswapd回收内存,直至high水位。