Linux 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,需要以下幾個操作:
-
設置內核參數memlock
memlock 用於限制用戶最大鎖定內存空間的大小,單位為KB。如果服務器只給Oracle 使用,那么這個參數的 值略小於物理內存即可。
# grep memlock /etc/security/limits.conf * soft memlock 16384000 * hard memlock 16384000
-
驗證Oracle用戶的memlock值
su - oracle ulimit -l
memlock是允許鎖定的內存空間。一般設置成與物理內存等同。
-
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;
修改后,重啟數據庫生效。
-
數據庫所有實例都啟動后,運行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
-
修改/etc/sysctl.conf
echo "vm.nr_hugepages = 4098" >> /etc/sysctl.conf
- 重啟數據庫
-
檢查大頁使用情況
grep -i huge /proc/meminfo
Created: 2020-04-29 Wed 16:27