常见的三个High Load的问题
1.直接内存回收引起的 load 飙高或者业务时延抖动
直接内存回收是在进程申请内存的过程中同步进行的回收,而这个回收过程可能会比较久,后续行为都被迫等待,这样就会造成很长时间的延迟和CPU 利用率的升高,最终引起 load 飙高。
DMA回收过程
在开始内存回收后,首先进行后台异步回收(下图中蓝色标记的地方),这不会引起进程的延迟;当后台异步回收跟不上进行内存申请的速度,就会开始同步阻塞回收,导致延迟(上图中红色和粉色标记的地方,这就是引起 load 高的地址)。
解决思路
我们可以适当增加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。
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过高的过程:
解决思路
控制好系统中积压的脏页数据。
我们需要控制好脏页数据的数量,脏页控制的少了可能会影响系统整体的效率,脏页控制的多了还是会触发问题,
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
内存管理的三个层次
内存管理从三个层次管理内存,分别是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水位。