CentOS 7 关闭透明大页

小柊 发表于 2019年04月30日 23时34分19秒

序、扯淡

上个月的笔者还在博客里吐槽工作忙没空写干货,本以为四月会好些,结果鬼知道四月依旧是工作满满,不得不感叹笔者平时广积德,现在正被修来的“福报”簇拥着呢。

 

一、背景

如果是一个全新安装好的CentOS 7 x64的系统,在没有调整过任何系统配置前,安装MongoDB后,每次进入MongoDB的shell时,都会被警告以下信息:

2019-04-30T14:41:40.953+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.

2019-04-30T14:41:40.953+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'

2019-04-30T14:41:40.953+0000 I CONTROL  [initandlisten]

2019-04-30T14:41:40.953+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.

2019-04-30T14:41:40.953+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'

2019-04-30T14:41:40.953+0000 I CONTROL  [initandlisten]

 

虽说除了每次进入MongoDB Shell时总是会被吃这么两个警告以外,其他功能貌似也没啥问题,心大的也就直接忽略了,但像笔者这样的强迫症看到这两个警告,总还是想把它去掉的。

那么MongoDB到底在警告我们什么呢?笔者上网查了一下,大概的意思就是MongoDB告诉你当前系统启用了透明大页,而建议你关掉它以提升性能。

 

二、什么是透明大页

那透明大页是什么,为什么MongoDB要这么指名道姓的告诉你把它关掉呢?

笔者在网上找到了一篇讲的非常详细的文章《Disable Transparent Hugepages》,由于原文是英文的,所以这里摘抄了国内翻译版

 

1.长话短说

“透明大页(Transparent Hugepages)”是一个Linux 内核特性,它通过提高处理器的内存映射硬件的使用效率(译注:降低TLB Miss 和page fault,提高TLB 的命中率,这部分基础知识可以翻下操作系统书)来获取更好的性能。在绝大多数Linux 发行版中是默认启用的(“enabled=always”)。

透明大页能让一些应用程序的性能提高一点点(最好的情况大约是10%,一般在0~3%),但是会造成很明显的性能问题(参考:MongoDBOracleSplunk), 甚至会造成严重的内存泄漏(参考:DigitalOceanGolang/Go).

 

为了避免这些问题,你应该将运行的服务器设置成enabled=madvise。

 

并在服务器的内核命令行上设置transparent_hugepage=madvise(例如在/etc/default/grub 中设置)。

完成这些修改之后,应用程序能通过透明大页提升性能,也能避免上述问题(译注:设置成madvise,应用程序通过设置MADV_HUGEPAGE标志就能分配到大页,不需要的程序则不受影响)。

 

2.什么是大页?

数十年来,处理器和操作系统之间通过使用虚拟内存,在应用程序可见的内存空间(虚拟内存空间(virtual address space))和底层的物理内存之间设置了间接层。间接层不仅保护了应用程序互相不受影响,还有许多强大的特性。

非常多的 x86 处理器都是通过一个叫页表(page table)的方案实现虚拟内存,这个方案会在内存中存一个非常大的映射表(实际上一个深度不同的树结构,不过也可以看作是一个稀疏表)。传统上,x86 处理器中一份页表条目对应一份4KB 的内存“页”。

虽然页表都是存在内存里,但是处理器会缓存一部分页表条目到处理器的寄存器上,它被称为TLB 。查看我笔记本上的cpuid(1)(译注:Linux tool to dump x86 CPUID information about the CPU(s))发现最低等级的TLB 只包含64个条目,每个条目对应一份4KB 的数据页。现在是2017年,64*4KB 只有四分之一兆字节,远小于目前使用的大多数应用程序的工作内存。这种大小不匹配的情况意味着占用大量内存的应用程序会周期性的遇到TLB 不命中的情况,从而需要花费很高的代价访问主内存,只为得到具体的内存地址(译注:TLB Miss 之后需要访问内存中的页表,从而得到具体内存地址)。

为了改进TLB 的效率,x86 及其他处理器长期以来都支持创建“大页(huge pages)”,大页的页表条目能映射一大段的物理内存地址。根据操作系统的配置不同,大多数最近的芯片能够映射2MB,4MB,甚至1GB 的内存页。使用大页意味着TLB 存着更多的数据,对某些特定的任务来说效率更高。

 

3.什么是透明大页?

存在各种页表管理方式,这意味着操作系统需要决定如何映射地址空间和物理内存。由于应用程序的内存管理接口(例如mmap(2))一直都是基于最小的 4KB 页,所以内核映射数据必须以4KB 为单位。最简单和最灵活的(就已支持的内存布局而言)方案是只采用4KB 的页,应用程序映射内存无法使用大页。长期以来,这是内核最通用的内存管理策略。

对于需要大量内存并对性能敏感的应用程序(例如某些特定数据库或者科学计算程序),内核引入hugetlbfs 特性,该特性允许系统管理员通过配置让特定的应用程序使用大页。

透明大页(简称“THP”),正如其名,旨在自动为应用程序提供大页支持,不需要特殊配置(译注:透明大页的透明,类似透明代理的透明)。透明大页通过在后台扫描(使用khugepaged 内核线程)内存映射,尝试找到或者创建(通过移动相邻的内存)总共2MB 的连续4KB 映射,用一个大页来替换这一段内存映射。

 

4.有什么问题?

透明大页运行良好时,特定的测试场景下,可以带来大约10% 的性能提升。然而,它也会造成至少两种非常严重的故障:

 

内存泄漏

THP 倾向于创建2MB 的内存映射。然而,这样做太贪心,即使必要的情况下,也不愿意把它们拆分回去。如果一个应用程序映射了一大段内存但是只访问前面几个字节,传统上只会消耗一个4KB 的物理内存页。THP 开启的情况下,khugepaged会将4KB 页扩张到2MB,内存占用量增大512倍(这份Bug 报告 中的例子更糟糕,甚至超过512 倍!)。

这种情况不是假设;Go 语言的GC 就有一个明确的解决方法,Digital Ocean 也记录了它们是如何处理Redis,THP 和jemalloc遇到的问题。

(译注:据说3.10 内核透明零页有泄漏问题,内核只释放2MB 中第一个4KB 的页面,剩余的页面泄漏)

 

卡顿和高CPU 使用率

应用程序都是分配相对静态的内存,稳定的状态下,khugepaged 的工作量是最小的。但是如果存在频繁映射内存的情况,或者存在生命周期很短的进程,khugepaged 会进行大量的拆分/合并内存区域的工作,毫无意义,存活时间很短。这会引起很高的CPU 使用率,以及较长的卡顿,因为内核被迫得先把2MB 的页拆分成4KB 的页,才能执行原本在单页上效率很高的操作。

因为这些原因,启用了THP 之后,有些应用程序都观察到30% 的性能下降,甚至更糟。

 

5.现在怎么办?

THP 作者们事先意识到了透明大页可能有潜在的问题(尽管如今看来,他们低估了问题的严重性),所以他们选择通过 /sys/kernel/mm/transparent_hugepage/enabled 系统文件配置透明大页。

更重要的是,他们为透明大页实现了一种可选择的模式。将/sys/kernel/mm/transparent_hugepage/enabled 设置为madvise,khugepaged 默认情况下不会处理内存,除非应用程序使用madvise 系统调用,给特定范围的内存进行THP 处理。

由于在大多数情况下,只有少数特定的应用程序能通过透明大页显著提升性能,所以这是一个两全其美的选项。这些少数的应用程序可以选择使用madvise,其余的应用程序不受影响。

所以,我建议每个用户都把透明大页配置成madvise,如文章开头tl;dr所说的。同时我也希望说服主流的发行版默认禁用透明大页,让更多的系统管理员和开发者避免踩这些坑。

 

三、怎么关闭?

上一章讲了透明大页的利弊,虽说透明大页能小幅度的优化程序性能,但在更多的情况下,透明大页还是会影响到各数据库软件的性能的,所以权衡以后,我们研发组决定关掉CentOS环境中的透明大页。

CentOS 7下关掉透明大页的方法有很多种,但我个人推荐以下做法:

 

1.手动关闭透明大页

在终端中使用下面两行命令即可:

 

注意:这样操作每次系统重启后,透明大页会重新开启,需要重新键入命令。

 

2.自动关闭透明大页

既然上面的两条命令需要每次开机时得重新输入,那咱们直接把这两条命令注册成系统服务,每次系统启动时自动运行不就好了?

 

上面的命令中,首先用tee命令创建了一个disable-thp的系统服务,然后命令daemon重新加载配置,使得systemd能够识别到我们新建的disable-thp服务,最后启动服务并设置服务开机自启动。

我之所以推荐这种方法是因为这种方式管理起来非常简单,如果以后需要暂时恢复原样,只需要禁用掉服务即可:

 

然后重启系统。

如果要决定要开回透明大页,不要再禁用了,直接删除服务文件并重启即可:

 

绿色,环保。

 

四、引用:

第三章中提到的方法出处:《How to disable Transparent Huge Pages on CentOS

 

 

小柊

2019年4月30日 23:20:40

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注