Linux 大页: HugePage 与transparent HugePage

Table of Contents

1 闲聊

有一段时间,数据库上出现过CPU消耗非常高的问题。最后分析到了Linux HugePage,发现自己对这一块都没什么了解。于是做了 些了解。

Linux 下的大页分为两种类型:标准大页(Huge Pages) 和透明大页(Transparent Huge Pages). 这两种是不一样的。

  • Huge Pages 从Linux Kernel 2.6 后被引入。目的是使用更大的页面(memory page size) 以适应越来越大的系统内存,让操作系统可以支持 现代硬件架构的大而面容量功能。一般单个大页的空间大于4k.
  • Transparent Huge Pages 从RHEL 6 .SUSE Server 11,Oracle Linux(UEK2) 开始引入的一个功能。

而这两种大页, 根本的区别:Huge Page 是预分配的,Transparent Huge Pages 是动态分配的。

在两者一起使用的情况下,可能会导致性能问题和系统重启。Oracle 服务器建议禁用Transparent Huge Pages. 而在Oracle Linux 6.5 中,已经删除Transparent HugePages.

2 关键概念

2.1 Page Table

保存着虚拟内存地址与物理内存地址之间的对应关系。所以在一个拥有虚拟内存的系统中,要访问物理内存地址,需要先访问page table.

2.2 TLB

TLB: Translation Lookaside Buffer. CPU 中的buffer空间,里面存储着Page Table 的部分内容。这部分空间大小是固定的。

2.3 hugetlb

TLB 中指向大页的entry.

2.4 hugetlbfs

Linux从内核2.6开始,出现这种是驻内存(in-memory)文件系统,如tmpfs. 在这种文件系统中分配的内存页,都是大页。

3 HugePage的优点

  • 不可交换 内存使用率比较高时,会避免或者减少page in / out.
  • 减轻TLB压力 因每个页面指向的物理内存范围变大,所以需要较少的Page table entry 就可以管理较大的内存空间。这也为查询带来好处, 原来需要查询100个page table entry,可能只需要1个entry 就足够了。
  • 减少页表空间占用 每个page talbe entry 可达64 bytes. 在管理50G内存时,lowmem 中pagetable大小为近800M,而实际上880M 都不一定用,因为 还要考虑lowmem 的其他用途(这是kernel 2.4中的情况,在kernel 2.6中,pagetable不是必须的)。如果使用了大页, 每个大页假设为256M,page table 40M 足矣。

4 HugePages

4.1 查看

  • 查询标准大页的大小

    之前说过,huge page 的大小是提前设置好的。那么设置的大页大小是多少呢。可以通过/proc/meminfo来看。

    $grep Huge /proc/meminfo
    
    AnonHugePages:     18432 kB
    HugePages_Total:       0
    HugePages_Free:        0
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    

    AnonHugePages: 匿名 HugePages 数量。Oracle Linux 6.5 中已删除此计数器。与透明 HugePages 有关。

    HugePages_Total: 分配的页面数目,和Hugepagesize相乘后得到所分配的内存大小

    HugePages_Free: 从来没有被使用过的Hugepages数目。即使oracle sga已经分配了这部分内存,但是如果没有实际写入,那么看到的还是Free的。这是很容易误解的地方(池中尚未分配的 HugePages 数量。)

    HugePages_Rsvd: 已经被分配预留但是还没有使用的page数目。在Oracle刚刚启动时,大部分内存应该都是Reserved并且Free的,随着ORACLE SGA的使用,Reserved和Free都会不断的降低

    HugePages_Surp: “surplus”的缩写形式,表示池中大于/proc/sys/vm/nr_hugepages 中值的 HugePages 数量。剩余 HugePages 的最大数量由 /proc/sys/vm/nr_overcommit_hugepages 控制。此值为0的情况很常见

    Hugepagesize: 页面大小

    HugePages_Free – HugePages_Rsvd 这部分是没有被使用到的内存,如果没有其他的oracle instance,这部分内存也许永远都不会被使用到,也就是被浪费了。

    HugePages_Total-HugePages_Free+HugePages_Rsvd 就是目前实例需要的页面数量.

  • 查看是否启用huge_page

    可以通过两个文件中的参数来确定,系统是否启用了大页。

    $ cat /proc/sys/vm/nr_hugepages
    0
    $ grep -i HugePages_Total /proc/meminfo
    HugePages_Total:       0
    

    如果两个的结果都是0 ,说明系统中已禁用大页,而且不存在内存大页

4.2 设置hugepage 大小

设置大页大小,一般都是修改内核参数nr_hugepages。即在/etc/sysctl.conf配置文件中设置参数vm.nr_hugepages。设置好后,重启服务器生效。

echo "vm.nr_hugepages=512" >> /etc/sysctl.conf

针对Oracle 数据库设置内存大页大小,参照 计算oracle大页大小.

  • 查看是否启用

    $cat /proc/sys/vm/nr_hugepages
    
    0
    
    $ grep -i HugePages_Total /proc/meminfo
    
    HugePages_Total:     0
    

    HugePages_Total为0,说明hugepage没有使用。 nr_hugepages为0,意味着没有设置。

5 Transparent Huge Pages

5.1 查看是否启用

# 针对redhat/CentOS6
cat /sys/kernel/mm/redhat_transparent_hugepage/enabled
# 针对其他系统及CentOS7开始
cat /sys/kernel/mm/transparent_hugepage/enabled

always madvise [never]

使用命令查看时,如果输出结果为[always]表示透明大页启用了。[never]表示透明大页禁用、[madvise]表示(只在MADV_HUGEPAGE标志的VMA中使用THP

通过grub.conf/rc.local配置文件来控制大页是否随系统启动.

通过调整/sys/kernel/mm/redhat_transparent_hugepage/enabled 文件实时控制是否使用透明大页。

5.2 不启用透明大页

5.2.1 grub

通过grub 来控制透明大页,是根本上不启用透明大页这一功能。

  • CentOS 6

    /etc/grub.conf 是系统启动时参数文件,大页随系统启动或者停止 ,可以通过参数transparent_hugepage 来控制。 transparent_hugepage = never 表示透明大页功能不随系统启动 transparent_hugepage = always 表示透明大页功能随系统启动

    vi /etc/grub.conf
    
    # grub.conf generated by anaconda
    #
    # Note that you do not have to rerun grub after making changes to this file
    # NOTICE:  You have a /boot partition.  This means that
    #          all kernel and initrd paths are relative to /boot/, eg.
    #          root (hd0,0)
    #          kernel /vmlinuz-version ro root=/dev/mapper/VolGroup--LogVol0-LogVol01
    #          initrd /initrd-[generic-]version.img
    #boot=/dev/sda
    
    default=0
    timeout=5
    splashimage=(hd0,0)/grub/splash.xpm.gz
    hiddenmenu
    title Red Hat Enterprise Linux 6 (2.6.32-504.el6.x86_64)
            root (hd0,0)
            kernel /vmlinuz-2.6.32-504.el6.x86_64 ro root=/dev/mapper/VolGroup--LogVol0-LogVol01 rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD SYSFONT=latarcyrheb-sun16 crashkernel=auto rd_LVM_LV=VolGroup-LogVol0/LogVol01 rd_LVM_LV=VolGroup-LogVol0/LogVol00  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
            initrd /initramfs-2.6.32-504.el6.x86_64.img
    transparent_hugepage=never
    
  • CentOS7/CentOS8

    在这两个版本中,grub的位置发生了变化。变为 /etc/default/grub,我们只需要将 transparent_hugepage=never 添加到GRUB_CMDLINE_LINUX 这个变量里即可。示例如下:

    GRUB_TIMEOUT=5
    GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
    GRUB_DEFAULT=saved
    GRUB_DISABLE_SUBMENU=true
    GRUB_TERMINAL_OUTPUT="console"
    GRUB_CMDLINE_LINUX="crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet transparent_hugepage=never"
    GRUB_DISABLE_RECOVERY="true"
    GRUB_ENABLE_BLSCFG=true
    

5.2.2 rc.local

通过rc.local 设置不启用,实际上是在开机的过程中先启用,然后再关闭。 下面是修改rc.local的脚本:

  • CentOS 6

    cat >> /etc/rc.local <<EOF
    if test -f /sys/kernel/mm/redhat_transparent_hugepage/enabled; then
    # 不使用
       echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
    # 使用
    #  echo always > /sys/kernel/mm/redhat_transparent_hugepage/enabled
    fi
    EOF
    
  • CentOS7-8

    cat >> /etc/rc.local <<EOF
    if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
    # 不使用
       echo never > /sys/kernel/mm/transparent_hugepage/enabled
    # 使用
    #  echo always > /sys/kernel/mm/transparent_hugepage/enabled
    fi
    EOF
    

其实这个脚本,只是将临时关闭透明大页的方法以脚本的方式保存,使系统启动时运行脚本,达到不使用透明在页的目的。

5.3 临时调整

具体方法参见临时关闭透明大页. 调整完,查看实际是否仍在使用:

[root@halberd1 ~]# grep HugePages_Total /proc/meminfo
HugePages_Total:       0

值为0说明没有使用大页。

6 大页与Oracle

由于Oracle 不建议使用透明大页,那么针对Oracle 若启用大页,则只能使用预留HugePages。 在Oralce 服务器上开启预留HugePages,需要以下几个操作:

  1. 设置内核参数memlock

    memlock 用于限制用户最大锁定内存空间的大小,单位为KB。如果服务器只给Oracle 使用,那么这个参数的 值略小于物理内存即可。

    # grep memlock /etc/security/limits.conf
    
    *    soft    memlock    16384000
    *    hard    memlock    16384000
    
  2. 验证Oracle用户的memlock值

    su - oracle
    ulimit -l
    

    memlock是允许锁定的内存空间。一般设置成与物理内存等同。

  3. Oracle数据库禁用AMM

    10G 版本Oracle 数据库可忽略此步骤。因为10G 版本中不存在此功能。

    11G 版本Oracle 数据库需要禁用AMM.

    11G 之后默认创建的实例会使用Automatic Memory Management (AMM)的特性,该特性与HugePage不兼容。

    使用AMM的情况下,所有的SGA 内存都是在/dev/shm 下分配的,因此在分配SGA时不会使用HugePage。这也是AMM 与HugePage不兼容的原因。 默认情况下ASM instance 也是使用AMM的,但因为ASM 实例不需要大SGA,所以对ASM 实例使用HugePages意义不大。

    禁用命令如下:

    alter system set memory_target=0 scope=both;
    alter system set memory_max_target=0 scope=spfile;
    

    修改后,重启数据库生效。

  4. 计算合适的大页数量

    数据库所有实例都启动后,运行hugepages_settings.sh(具体参考Mos文档Document 401749.1)以计算出合适的大页数量。

    #!/bin/bash
    #
    # hugepages_settings.sh
    #
    # Linux bash script to compute values for the
    # recommended HugePages/HugeTLB configuration
    # on Oracle Linux
    #
    # 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.
    #
    # This script is provided by Doc ID 401749.1 from My Oracle Support
    # http://support.oracle.com
    
    # Welcome text
    echo "
    This script is provided by Doc ID 401749.1 from My Oracle Support
    (http://support.oracle.com) where it is intended to compute values for
    the recommended HugePages/HugeTLB configuration for the current shared
    memory segments on Oracle Linux. Before proceeding with the execution please note following:
     * For ASM instance, it needs to configure ASMM instead of AMM.
     * The 'pga_aggregate_target' is outside the SGA and
       you should accommodate this while calculating the overall size.
     * In case you changes the DB SGA size,
       as the new SGA will not fit in the previous HugePages configuration,
       it had better disable the whole HugePages,
       start the DB with new SGA size and run the script again.
    And make sure that:
     * Oracle Database instance(s) are up and running
     * Oracle Database 11g Automatic Memory Management (AMM) is not setup
       (See Doc ID 749851.1)
     * The shared memory segments can be listed by command:
         # ipcs -m
    
    
    Press Enter to proceed..."
    
    read
    
    # 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}'`
    if [ -z "$HPG_SZ" ];then
        echo "The hugepages may not be supported in the system where the script is being executed."
        exit 1
    fi
    
    # Initialize the counter
    NUM_PG=0
    
    # Cumulative number of pages required to handle the running shared memory segments
    for SEG_BYTES in `ipcs -m | cut -c44-300 | awk '{print $1}' | 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
    
    RES_BYTES=`echo "$NUM_PG * $HPG_SZ * 1024" | bc -q`
    
    # An SGA less than 100MB does not make sense
    # Bail out if that is the case
    if [ $RES_BYTES -lt 100000000 ]; then
        echo "***********"
        echo "** ERROR **"
        echo "***********"
        echo "Sorry! There are not enough total of shared memory segments allocated for
    HugePages configuration. HugePages can only be used for shared memory segments
    that you can list by command:
    
        # ipcs -m
    
    of a size that can match an Oracle Database SGA. Please make sure that:
     * Oracle Database instance is up and running
     * Oracle Database 11g Automatic Memory Management (AMM) is not configured"
        exit 1
    fi
    
    # 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') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
        '3.8') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
        '3.10') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
        '4.1') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
        '4.14') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
        *) echo "Kernel version $KERN is not supported by this script (yet). Exiting." ;;
    esac
    
    

    示例:

    For 2.4 kernel systems:
    $ ./hugepages_settings.sh
    ...
    Recommended setting: vm.hugetlb_pool = 764
    
    For 2.6 and later kernel systems:
    $ ./hugepages_settings.sh
    ...
    Recommended setting: vm.nr_hugepages = 67
    
  5. 修改/etc/sysctl.conf

    echo "vm.nr_hugepages = 4098" >> /etc/sysctl.conf
    
  6. 重启数据库
  7. 检查大页使用情况

    grep -i huge /proc/meminfo
    

Author: halberd

Created: 2020-04-29 Wed 16:27

Validate