乐趣区

关于tidb:我们为什么要禁用-THP

前言

咱们之前在生产环境上遇到过很多起由操作系统的某些特色引起的性能抖动案例,其中 THP 作案次数较多,因而本文将和大家分享 THP 引起性能抖动的起因、典型的景象,分析方法等,在文章的最初给出应用 THP 时的配置倡议及敞开办法。

THP(Transparent Huge Page)简介

世界并不是非黑即白的,THP 也是内核的一个重要特色,且继续在演进,其目标是通过将页表项映射更大的内存,来缩小 Page Fault,从而晋升 TLB(Translation Lookaside Buffer,由存储器治理单元用于改良虚拟地址到物理地址的转译速度)的命中率。联合存储器层次结构设计原理可知,当程序的访存局部性较好时,THP 将带来性能晋升,反之 THP 的劣势不仅丢失,还有可能化身为恶魔,引起零碎的不稳固。遗憾的是数据库的负载拜访特色通常是离散的。

Linux 内存治理回顾

在陈说 THP 引起的负面景象前,先来和大家一起回顾下,Linux 操作系统是如何治理物理内存的。对于不同的体系结构,内核对应不同的内存布局图。其中用户空间通过多级页表进行映射来节约映射管理所需的空间,而内核空间为了简略高效采纳线性映射。在内核启动时,物理页面将退出到搭档零碎(Buddy System)中,用户申请内存时调配,开释时回收。为了关照慢速设施及兼顾多种 workload,Linux 将页面类型分为匿名页(Anon Page)和文件页(Page Cache),及 swapness,应用 Page Cache 缓存文件(慢速设施),通过 swap cache 和 swapness 交由用户依据负载特色决定内存不足时回收二者的比例。为了尽可能快的响应用户的内存申请需要并保证系统在内存资源缓和时运行,Linux 定义了三条水位线(high,low,min),当残余物理内存低于 low 高于 min 水位线时,在用户申请内存时通过 kswapd 内核线程异步回收内存,直到水位线复原到 high 以上,若异步回收的速度跟不上线程内存申请的速度时,将触发同步的间接内存回收,也就是所有申请内存的线程都同步的参加内存回收,一起将水位线抬上去后再取得内存。这时,若须要回收的页面是洁净的,则同步引起的阻塞工夫就比拟短,反之则很大(比方几十、几百 ms 甚至 s 级,取决于后端设施速度)。除水位线外,当申请大的间断内存时,若残余物理内存短缺,但碎片化比较严重时,内核在做内存规整的时候,也有可能触发间接内存回收(取决于碎片化指数,前面会介绍)。因而内存间接回收和内存规整是过程申请内存门路上的可能遇到的次要提早。而在访存局部性差的负载下,THP 将成为触发这两个事件的幕后黑手。

最典型特色 —— Sys CPU 使用率飙升

咱们在多个用户现场发现当调配 THP 引发性能稳定时,其最典型的特色就是 Sys CPU 使用率飙升,这种特色的剖析比较简单,通过 perf 抓取 on-cpu 火焰图,咱们就能够看到咱们服务所有处于 R 状态的线程都在做内存规整,且缺页异样处理函数为 do_huge_pmd_anonymous_page,阐明以后零碎没有间断 2M 的物理内存,因而触发了间接内存规整,间接内存规整的逻辑是很耗时的,是导致 sys 利用率升高的起因。

间接特色—— Sys load 飙升

实在的零碎往往是简单的,当调配 THP 或调配其它高阶内存时,零碎并不会做间接内存规整,留下上述那么典型的立功特色,而是混合其余行为,比方间接内存回收。间接内存回收的参加让事件变的略微有些简单和令人纳闷,比方咱们最后从客户现场看到 normal zone 的残余物理内存高于 high 水位线,可零碎为啥不停的在做间接内存回收呢?咱们深刻到慢速内存调配的解决逻辑中可知,慢速内存调配门路次要有几个步骤:1. 异步内存规整;2. 间接内存回收;3. 间接内存规整;4. oom 回收,每个步骤解决实现后,都会尝试分配内存,如果可调配了,则间接返回页面,略过前面的局部。其中内核为搭档零碎的每个 order 提供了碎片指数来示意内存调配失败是因为内存不足还是碎片化引起的。和其关联的是 /proc/sys/vm/extfrag_threshold, 当靠近 1000 时,示意调配失败次要和碎片化相干,此时内核会偏向于做内存规整,当靠近 0 时,示意调配失败和内存不足关联更大,则内核会偏向于做内存回收。因而产生了在高于 high 水位线的时候,频繁进行间接内存回收的景象。而因为 THP 的开启和应用占据了高阶内存,因而减速了内存碎片化引起的性能抖动问题。

对此特色,断定办法如下:

  1. 运行 sar -B 察看 pgscand/s,其含意为每秒产生的间接内存回收次数,当在一段时间内继续大于 0 时,则应继续执行后续步骤进行排查;
  2. 运行 cat /sys/lernel/debug/extfrag/extfrag_index 察看内存碎片指数,重点关注 order >= 3 的碎片指数,当靠近 1.000 时,示意碎片化重大,当靠近 0 时示意内存不足;
  3. 运行 cat /proc/buddyinfo, cat /proc/pagetypeinfo 查看内存碎片状况,指标含意参考(https://man7.org/linux/man-pa…),同样关注 order >= 3 的残余页面数量,pagetypeinfo 相比 buddyinfo 展现的信息更具体一些,依据迁徙类型(搭档零碎通过迁徙类型实现反碎片化)进行分组,须要留神的是,当迁徙类型为 Unmovable 的页面都汇集在 order < 3 时,阐明内核 slab 碎片化重大,咱们须要联合其余工具来排查具体起因,在本文就不做过多介绍了;
  4. 对于 CentOS 7.6 等反对 BPF 的 kernel 也能够运行咱们研发的 drsnoop,compactsnoop 工具对提早进行定量分析,应用办法和解读形式请参考对应文档;
  5. (Opt) 应用 ftrace 抓取 mm_page_alloc_extfrag 事件,察看因内存碎片从备用迁徙类型“盗取”页面的信息;

非典型特色—— 异样的 RES 使用率

咱们在 AARCH64 服务器上,遇到过服务刚启动就占用几十个 G 物理内存的场景,通过观察 /proc/pid/smaps 文件能够看到内存大部分用于 THP,且 AARCH64 的 CentOS 7 内核编译时选用的 PAGE SIZE 为 64K,因而相比 X86_64 平台的内存用量差出很多倍。在定位的过程中咱们也顺便修复了 jemalloc 未齐全敞开 THP 的 bug: fix opt.thp:never still use THP with base_map。

结语

对于未对访存局部性进行优化的程序或负载自身就是离散的访存程序而言,将 THP 以及 THP defrag 设置为始终开启,对长时间运行的服务而言有害无益,且内核从 4.6 版本内核版本起才对 THP 的 defrag 提供了 defer,defer + madvise 等优化。因而对于咱们罕用的 CentOS 7 3.10 版本的内核来说,若程序须要应用 THP,则倡议将 THP 的开关设置为 madvise,在程序中通过 madvise 零碎调用来调配 THP,否则设置成 never 禁用掉是最佳抉择:

查看以后的 THP 模式:

cat /sys/kernel/mm/transparent_hugepage/enabled

若值是 always 时,执行:

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

实现敞开操作。
须要留神的是为避免服务器重启生效,应将这两个命令写入到 .sevice 文件中,交给 systemd 进行治理。

退出移动版