数据库服务器开启内存大页优化及机制

发布时间 2023-11-22 17:42:18作者: 橙皮^-^

一、背景

在一次Oracle数据库健康检查报告中,显示PageTables所占用内存过大,建议配置大页
PageTables(页表):用于将内存的虚拟地址翻译成物理地址,随着内存地址分配得越来越多,这个需要从Linux分页了解起

二、Linux 分页

在计算机操作系统中,内存分页是一种内存管理方案,也是现代操作系统中虚拟内存实现的重要组成部分。

2.1 Linux 32 位 三层级分页映射流程[1]

默认页大小4kb,因此offset偏移位数为 12位。

CR3是CPU的寄存器,把每个活跃的进程页目录放进CR3寄存器中,开始地址转换工作
可以寻址的内存空间大小:

\[2^2(页目录项表)*2^9(页目录)*2^9(页表)*4KB(默认页大小) = 4GB \]

2.2 目前Linux 五级分页结构[2]

  • PGD 页全局目录(Page Global Directory):
  • P4D 第四层级目录(Page Level 4 Directory)
  • PUD 页上级目录(Page Upper Directory)
  • PMD 页中间目录(Page Middle Directory)
  • PTE 页表项(Page Table Entry):每一个页表项元素,将虚拟地址内存映射到物理内存的一个页面,每一个页表项的大小为32位或64。

新系统分页增加多个层级,可以寻址的地址空间也会随着增大。

三、一个进程 PageTables 大小计算

\[pagetables = usedmemory(已使用内存)/page size(页大小) * \quad PTE\quad SIZE(页表项大小) \]

  • Linux系统 可以通过以下指令查看默认内存页大小,默认为4KB
$ getconf PAGESIZE
//4096

通过计算公式可以得知,通过增加页大小,可以有效减少pagetables占用内存大小。
以Oracle服务器为例子,一般SGA设置会比较大,假定设置为64GB,且没有设置大页内存,按照默认内存页为4KB,64位操作系统页表项大小为(PTE) 8B,那么\(pagetables\)的大小为

\[64GB / 4KB * 8B = 128MB \]

在实际生产环境中,Oracle数据库的链接进程比较多,每个进程都有自己的页表,如果每个进程都用到SGA的全部内存,那么按照上面计算,假设由100个进程,那么页表占用的内存空间就会达到\(12.5GB\),在实际中数据库健康检查报告中占用页表内存也快达到\(10GB\),因此在Oracle最佳实践中,建议配置大页内存。

  • 查看当前系统页表内存占用空间大小
# grep PageTables /proc/meminfo
PageTables:      1244880 kB
#

四、配置大页内存[3]

  • 查看当前系统大页配置情况,以及默认大页大小,Linux默认大页大小为2MB
$ grep Huge /proc/meminfo
AnonHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
$
  • 使用以下脚本计算当前所需要的大页数量
#!/bin/bash
#
# hugepages_setting.sh
#
# Linux bash script to compute values for the
# recommended HugePages/HugeTLB configuration
#
# Note: This script does calculation for all shared memory
# segments available when the script is run, no matter it
# is an Oracle RDBMS shared memory segment or not.
# Check for the kernel version
KERN=`uname -r | awk -F. '{ printf("%d.%d\n",$1,$2); }'`
# Find out the HugePage size
HPG_SZ=`grep Hugepagesize /proc/meminfo | awk {'print $2'}`
# Start from 1 pages to be on the safe side and guarantee 1 free HugePage
NUM_PG=1
# Cumulative number of pages required to handle the running shared memory segments
for SEG_BYTES in `ipcs -m | awk {'print $5'} | grep "[0-9][0-9]*"`
do
   MIN_PG=`echo "$SEG_BYTES/($HPG_SZ*1024)" | bc -q`
   if [ $MIN_PG -gt 0 ]; then
      NUM_PG=`echo "$NUM_PG+$MIN_PG+1" | bc -q`
   fi
done
# Finish with results
case $KERN in
   '2.4') HUGETLB_POOL=`echo "$NUM_PG*$HPG_SZ/1024" | bc -q`;
          echo "Recommended setting: vm.hugetlb_pool = $HUGETLB_POOL" ;;
   '2.6' | '3.8' | '3.10' | '4.1' | '4.14' ) echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
    *) echo "Unrecognized kernel version $KERN. Exiting." ;;
esac
# End
  • 修改文件执行权限,在运行Oracle服务器上执行脚本
$ chmod u+x hugepages_setting.sh
$ ./hugepages_setting.sh
Recommended setting: vm.nr_hugepages = 4819
$
  • 使用root用户,修改"/etc/sysctl.conf"文件,增加下面语句,分配数量需要比脚本输出的值多1-2个备用页
vm.nr_hugepages=4821
  • 执行以下命令使修改生效,需要注意在数据库服务器上,需要提前检查是否有足够内存分配,如果正在运行数据库,可以先关闭数据库释放内存,再接着执行
$ sysctl -p
  • 或者可以修改内核配置文件"/etc/grub.conf",增加"hugepages=4821"在末尾处,需要重启机器生效。

执行完以上指令再查看大页配置,可以看到新配的大页内存,但是还没用使用到

$ grep Huge /proc/meminfo
AnonHugePages:    374784 kB
HugePages_Total:    4821
HugePages_Free:     4821
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
$
  • 修改文件"/etc/security/limits.conf" 或 "/etc/security/limits.d/99-grid-oracle-limits.conf",添加以下字段。设置值至少是大页分配的内存值,在这里例子值是 \(4821*2048 = 9873408\)
* soft memlock 9873408
* hard memlock 9873408
  • 重启数据库,使配置生效,重新查看大页使用情况,这里可以看到大页已经被使用到了
$ grep Huge /proc/meminfo
AnonHugePages:    407552 kB
HugePages_Total:    4821
HugePages_Free:       19
HugePages_Rsvd:       15
HugePages_Surp:        0
Hugepagesize:       2048 kB
$
  • 配置Oracle 强制使用大页内存,需要Oracle版本11.2.0.2或更新,设置初始化参数"USE_LARGE_PAGES" 为 ”only",若系统没有配置大页内存,或者分配内存不够,启动数据库时会检查报错不让启动
ALTER SYSTEM SET use_large_pages=only SCOPE=SPFILE;
SHUTDOWN IMMEDIATE;sh
STARTUP;
  • 若没有配置大页或空间不足够,启动时将会显示下面告警信息
SQL> STARTUP
ORA-27137: unable to allocate large pages to create a shared memory segment
Linux-x86_64 Error: 12: Cannot allocate memory
SQL> 
  • 到此就配置完成。

五、大页内存的优缺点

除了以上Oracle使用大页可以减少页表占用内存大小外,还有以下优点

  • 在TLB容量固定的情况下,提高TLB的命中率。大页2MB,同样大小的内存,换成4KB大小页,需要512项TLB条目。
  • 减少了页表级数,也可以减少查找页表的时间。大页使得页表级数减少,例如原来多达4级的页表可以减少到2级。查找时间会改善。
  • 减少缺页异常(page fault)的发生次数。系统按页大小为单位加载内存,2MB,大页只需要一次异常缺页就可以加载,而4KB需要512次缺页异常才能将2MB全部映射到物理内存。这也减少IO次数。

缺点:

  • 大页的使用需要应用程序从代码上设置管理大页内存。

五、总结

  • 在Oracle服务器上配置开启大页配置优化,可以有效减少\(pagetable\)占用内存大小。
  • Oracle设置初始化参数“USE_LARGE_PAGES”为“only"可以在强制使用大页内存,若没有足够大页存在,会报错不让启动,可以作为启动环境检查措施。
  • 大页内存可以让应用程序性能更优,不过需要应用程序适配。

六、参考


  1. https://en.wikipedia.org/wiki/Page_table#Page_table_entry ↩︎

  2. https://docs.kernel.org/mm/page_tables.html ↩︎

  3. https://oracle-base.com/articles/linux/configuring-huge-pages-for-oracle-on-linux-64 ↩︎