本文主要阐述Linux中HugePages的作用,当然可能很多人知道HugePages可以提升数据库性能,本文其实是要阐述HugePages为什么可以提升数据库性能。在介绍正式内容之前,先介绍一些前驱知识作为铺垫。想要知道为什么HugePages为什么可以提升数据库性能就需要知道系统是如何进行内存管理的。所以本文先会介绍虚拟内存相关的知识。
早期的内存管理
CPU和内存都是比较宝贵的资源,那么如何利用这些资源呢。我们一个最直观的想法就是CPU直接访问物理内存就好了。确实最早的时候,CPU访问内存确实是访问的物理内存。但是CPU直接访问物理内存会有如下问题:
- 一个程序可能访问另一个程序的内存,造成程序运行错误甚至崩溃
- 由于都可以直接访问物理内存,甚至可能造成系统崩溃等问题
- 受限于实际的物理内存的大小,如果一个程序使用内存较大,很可能分给的物理内存是不连续
虚拟内存
那么现代内存管理技术是如何解决上面的提到的问题的呢?这就要提到虚拟内存技术了。
简单的说,就是在进程和物理内存直接增加了一层虚拟内存,而不是去直接操作物理内存。
虚拟内存是计算机系统内存管理的一种技术,我们可以手动设置自己电脑的虚拟内存。不要单纯认为虚拟内存只是“使用硬盘空间来扩展内存“的技术。虚拟内存的重要意义是它定义了一个连续的虚拟地址空间,并且 把内存扩展到硬盘空间。
使用虚拟内存的意义在于,不仅仅是将内存拓展到了硬盘上,扩大了内存。通常我们看到各种程序使用的内存的总数是大于我们机器的实际内存的,之所以这样,就是因为使用了虚拟内存技术。
使用虚拟内存有如下好处:
- 程序拥有联系的虚拟内存空间,并以为自己独享内存空间,当然实际映射到物理内存上可能是不连续的
- 由于虚拟内存可以使程序间互相“隔离”,这样更安全
于是,这里就引出了两种地址的概念:
- 我们程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address)
- 实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)。
操作系统引入了虚拟内存,进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存,如下图所示:
内存管理
常见的内存管理方式有段式管理和页式管理,具体怎么管理的见20 张图揭开「内存管理」的迷雾,瞬间豁然开朗的内存分段和内存分页小节的详细描述,本文不做过多细节介绍。
内存分页
内存分段的好处就是能产生连续的内存空间,但是会出现内存碎片和内存交换的空间太大的问题。
要解决这些问题,那么就要想出能少出现一些内存碎片的办法。另外,当需要进行内存交换的时候,让需要交换写入或者从磁盘装载的数据更少一点,这样就可以解决问题了。这个办法,也就是内存分页(Paging)。
分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫页(Page)。在 Linux 下,每一页的大小为 4KB
。
虚拟地址与物理地址之间通过页表来映射,如下图:
内存映射
页表实际上存储在 CPU 的内存管理单元 (MMU) 中,于是 CPU 就可以直接通过 MMU,找出要实际要访问的物理内存地址。
而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。
分页是怎么解决分段的内存碎片、内存交换效率低的问题?
由于内存空间都是预先划分好的,也就不会像分段会产生间隙非常小的内存,这正是分段会产生内存碎片的原因。而采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。
如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出(Swap Out)。一旦需要的时候,再加载进来,称为换入(Swap In)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。
HugePages
HugePages顾名思义,除了标准的 4KB 大小的页面外,它们还能帮助管理内存中的巨大的页面。使用“大内存页”,你最大可以定义 1GB 的页面大小。
在系统启动期间,你能用“大内存页”为应用程序预留一部分内存。这部分内存,即被“大内存页”占用的这些存储器永远不会被交换出内存。它会一直保留其中,除非你修改了配置。这会极大地提高像 Oracle 数据库这样的需要海量内存的应用程序的性能。
2MB 一般都是 HugePages 的默认大小,在 arm64 和 x86_64 的架构上甚至支持 1GB 的大页面,是 Linux 默认页面大小的 262,144 倍,我们可以使用如下所示的命令查看当前机器上 HugePages 的相关信息:
1 | $ cat /proc/meminfo | grep Huge |
通过上面的输出结果,我们可以看到当前机器上的大页面默认大小为 2MB 并且大页面的数量也为 0,即没有进程在申请或者使用大页。
为什么使用HugePages可以提升数据库的性能?
通过上文对内存分页的介绍,可以知道:在虚拟内存管理中,内核维护一个将虚拟内存地址映射到物理地址的表,对于每个页面操作,内核都需要加载相关的映射。如果你的内存页很小,那么你需要加载的页就会很多,导致内核会加载更多的映射表。而这会降低性能。
使用“大内存页”,意味着所需要的页变少了。从而大大减少由内核加载的映射表的数量。这提高了内核级别的性能最终有利于应用程序的性能。
简而言之,通过启用“大内存页”,系统具只需要处理较少的页面映射表,从而减少访问/维护它们的开销!
- HugePages 可以降低内存页面的管理开销,可以减少进程中的页表项
- HugePages 提高 TLB 缓存的命中率和内存的访问效率
- HugePages 可以锁定内存,禁止操作系统的内存交换和释放,不会被交换到磁盘上为其它请求让出内存
正是上面提单的三点原因,所以我们可以通过使用HugePages来提升数据库这种占用内存大的应用的性能。