Linux的内存管理系统对用户是透明的。但是,如果您不熟悉其工作原理,则可能会遇到意外的性能问题。对于复杂的软件(例如数据库)尤其如此。当数据库在Linux中运行时,即使很小的系统差异也可能影响性能。
经过深入调查,我们发现Linux内存管理功能“透明大页面(THP)”通常会降低数据库性能。在本文中,我将介绍THP如何导致性能波动,典型症状以及我们推荐的解决方案。
THP是Linux内核的重要功能。它将页表条目映射到更大的页面大小以减少页面错误。这样可以提高转换后备缓冲区(TLB)的命中率。 TLB是内存管理单元用来提高从虚拟内存地址到物理内存地址的转换速度的内存缓存。
当正在访问的应用程序数据连续时,THP通常可以提高性能。相反,如果内存访问模式不连续,则THP无法履行其职责,甚至可能导致系统不稳定。
不幸的是,已知数据库工作负载具有稀疏而不是连续的内存访问。因此,您应该为数据库禁用THP。
为了了解THP可能造成的危害,让我们考虑Linux如何管理其物理内存。
对于不同的体系结构,Linux内核采用不同的内存映射方法。其中,用户空间通过多级分页来映射内存以节省空间,而内核空间使用线性映射来实现简单性和高效率。
当内核启动时,它将物理页面添加到伙伴系统。每次用户申请内存时,伙伴系统都会分配所需的页面。当用户释放内存时,伙伴系统会取消分配页面。
为了适应低速设备和各种工作负载,Linux将内存页面分为匿名页面和基于文件的页面。 Linux使用页面缓存为低速设备缓存文件。当内存不足时,用户可以使用交换缓存和swappiness指定要释放的两种类型页面的比例。
为了尽快响应用户的内存应用程序并确保在内存资源不足时系统正常运行,Linux定义了三个水印:高,低和最小。
如果未使用的物理内存小于不足且大于最小值,则当用户申请内存时,页面替换守护程序kswapd异步释放内存,直到可用的物理内存大于上限。
如果异步内存回收无法跟上内存应用程序的速度,则Linux会触发同步直接回收。在这种情况下,所有与内存相关的线程都会同步参与释放内存。当有足够的内存可用时,线程将开始获取其申请的内存空间。
在直接回收期间,如果页面是干净的,则由同步回收引起的阻塞很短;否则,可能会导致数十毫秒的延迟,并且取决于后端设备,有时甚至会是几秒钟。
除水印外,另一种机制也可能导致直接回收内存。有时,线程适用于大部分连续的内存页面。如果有足够的物理内存,但是碎片化,内核将执行内存压缩。这也可能触发直接的内存回收。
综上所述,当线程申请内存时,延迟的主要原因是直接内存回收和内存压缩。对于内存访问不是非常连续的工作负载(例如数据库),THP可能会触发这两个任务,从而导致性能波动。
如果系统性能波动,如何确定THP是原因?我想分享我们发现的与THP相关的三个症状。
根据我们的客户支持经验,THP引起的性能波动的最典型症状是急剧提高系统CPU利用率。
在这种情况下,如果您使用perf创建on-cpu火焰图,则会看到所有处于可运行状态的服务线程都在执行内存压缩。另外,页面错误异常处理程序是do_huge_pmd_anonymous_page。这意味着当前系统没有2 MB的连续物理内存,并且触发了直接内存压缩。直接内存压缩非常耗时,因此会导致较高的系统CPU利用率。
许多内存问题并不像上述问题那么明显。当系统分配内存或其他高级内存时,它不会直接执行内存压缩,而给您留下明显的痕迹。相反,它通常将压缩与其他任务(例如直接内存回收)混合在一起。
在此过程中涉及直接回收,使我们的故障排除更加复杂。例如,当正常区域中未使用的物理内存高于高水位标记时,系统仍将继续回收内存。要深入了解这一点,我们需要更深入地研究慢速内存分配的处理逻辑。
在每个步骤之后,系统都会尝试分配内存。如果分配成功,系统将返回分配的页面,并跳过其余步骤。对于每个分配,内核为伙伴系统中的每个订单提供一个碎片索引,该索引指示分配失败是由内存不足还是由碎片内存引起的。
碎片索引与/ proc / sys / vm / extfrag_threshold参数关联。该数字越接近1000,分配失败与内存碎片的相关性就越大,内核执行内存压缩的可能性就越大。该数字越接近0,分配失败与内存不足的关联就越多,内核更倾向于执行内存回收。
因此,即使未使用的内存高于高水位标记,系统也会频繁回收内存。由于THP消耗了高级内存,因此加剧了内存碎片导致的性能波动。
查看每秒执行的直接内存回收操作。执行sar -b观察pgscand / s。如果此数字在连续的一段时间内大于0,请执行以下步骤来解决问题。
观察内存碎片索引。执行cat / sys / kernel / debug / extfrag / extfrag_index以获取索引。关注顺序为≥3的块的碎片索引。如果数目接近1,000,则碎片严重;否则,碎片严重。如果接近0,则表示内存不足。
查看内存碎片状态。执行cat / proc / buddyinfo和cat / proc / pagetypeinfo以显示状态。 (有关详细信息,请参阅Linux手册页。)关注顺序为> = 3的页面数。
与buddyinfo相比,pagetypeinfo显示按迁移类型分组的更详细的信息。伙伴系统通过迁移类型实现防碎片。请注意,如果所有“不可移动”页面都按< 3,内核slab对象有严重的碎片。在这种情况下,您需要使用其他工具排除问题的具体原因。
对于支持Berkeley数据包过滤器(BPF)的内核,例如CentOS 7.6,您还可以使用PingCAP开发的drsnoop或compactsnoop对延迟进行定量分析。
(可选)使用ftrace跟踪mm_page_alloc_extfrag事件。由于内存碎片,迁移类型会从备份迁移类型中窃取物理页面。
有时,当服务在AARCH64服务器上启动时,会占用数十GB的物理内存。通过查看/ proc / pid / smaps文件,您可能会看到大多数内存用于THP。由于AARCH64的CentOS 7内核将其页面大小设置为64 KB,因此其常驻内存使用量比x86_64平台大很多倍。
对于未优化为连续存储数据的应用程序或工作负载稀疏的应用程序,启用THP和THP碎片整理会对长期运行的服务有害。
在Linux v4.6之前,内核不提供THP碎片整理的defer或defer + madvise。因此,对于使用v3.10内核的CentOS 7,建议禁用THP。但是,如果您的应用程序确实需要THP,建议您将THP设置为madvise,这将通过madvise系统调用来分配THP。否则,将THP设置为从不是您应用程序的最佳选择。
请注意,如果重新启动服务器,则THP可能会再次打开。您可以在.service文件中编写两个命令,然后让systemd为您管理它。
如果您对数据库性能调整还有其他疑问,或者想分享您的专业知识,请随时加入TiDB Community Slack工作区。