Hugepages関連の設定を試す（Oracle Database 19c）

# Hugepagesとは？

HugepagesはLinuxのメモリー管理機能の一部です（他のOSでも似た機能を持っています）。Linuxでは通常4KB単位でメモリーをページテーブルで管理します。しかしページテーブルのサイズは有限であり、大規模メモリー環境ですべてのメモリーを4KB単位で管理すると、ページテーブルの管理負荷やTLB (Translation Lookaside Buffer) キャッシュのヒット率が下がるなどの弊害があります。
このためメモリーを管理するページのサイズを拡大（2MB）して、ページテーブルを小さくし、TLBのヒット率を上げる機能が提供されました（Hugepages）。RDBMSは大容量のメモリーを必要とするため、Hugepagesの恩恵を受けやすいといえます。このためOracle Databaseをはじめ、多くのRDBMSでHugePagesの利用をサポートしています。

Hugepagesには利点もありますが、欠点もあります。

- スワップアウトされません（利点にもなります）。
- アプリケーションが利用を意識する必要があります。
- 通常のメモリー領域とHugepages領域の両方を管理する必要があります。

Oracle DatabaseではSGAの領域をHugepages上のマップすることができます。制約としては、AMM (Automatic Memory Management) 機能が利用できません。このため初期化パラメーターmemory_target, memory_max_targetに0を指定する必要があります。

```
SQL> ALTER SYSTEM SET memory_target=0 SCOPE=SPFILE;
System altered.

SQL> ALTER SYSTEM SET memory_max_target=0 SCOPE=SPFILE;
System altered.

SQL> EXIT
$
$ srvctl stop database -d O19A
$ srvctl start database -d O19A
$
```

# Transparent Hugepages
 Hugepagesの利用はアプリケーションが意識する必要があります。例えばmmapシステムコールのマップをHugepages領域に置くには、MAP_HUGETLBをパラメーターに指定する必要があります。アプリケーションにHugepagesを意識させずに利用させる機能がTransparent Hugepagesですが、Oracle Databaseでは利用は推奨されません。[マニュアル](https://docs.oracle.com/cd/F19136_01/unxar/administering-oracle-database-on-linux.html#GUID-02E9147D-D565-4AF8-B12A-8E6E9F74BEEA) には以下のように示されています。

> インストールを開始する前に、透過的なHugePagesを無効にすることをお薦めします。


Red Hat Enterprise Linux 7 でTransparent Hugepagesを常に無効にするには、/etc/default/grubファイルを修正します。GRUB_CMDLINE_LINUXパラメーターにtransparent_hugepage=neverを追記します。その後grub2-mkconfigコマンドを実行し、OSを再起動します。

```
# vi /etc/default/grub
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="rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet transparent_hugepage=never"
GRUB_DISABLE_RECOVERY="true"
#
# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-957.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-957.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-fba6994c21b54b7a8921e313cde69169
Found initrd image: /boot/initramfs-0-rescue-fba6994c21b54b7a8921e313cde69169.img
done
# 
```

Transparent Hugepagesが無効であることを確認します。

```
# cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]
```

# ユーザー設定
Hugepagesを利用するユーザー（Oracle Databaseでは通常oracleユーザー）にHugepagesの利用量制限を指定します。memlock制限の容量を物理メモリーよりも少しだけ少ない値（90% 以上）に指定します。

```
# grep MemTotal /proc/meminfo
MemTotal:        8174100 kB
#
# grep memlock /etc/security/limits.conf
oracle  soft    memlock 8100000
oracle  hard    memlock 8100000
#
# su - oracle
$ ulimit -l
8100000
$
```

# Hugepagesのサイズ
 Hugepagesのサイズは、カーネルパラメーターvm.nr_hugepagesにページ数を指定します。
適切な値は、[マニュアル]() に記載されている以下のスクリプトを実行します。一部 Red Hat Enterprise Linux 7.6用にカーネルのバージョンを修正しています（3.8 -> 3.10)。

```
# cat hugepages_settings.sh

#!/bin/bash
#
# hugepages_settings.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.10') echo "Recommended setting: vm.nr_hugepages = $NUM_PG" ;;
    *) echo "Unrecognized kernel version $KERN. Exiting." ;;
esac
# End
```

スクリプトとして保存し、実行すると現在稼働しているOracle Databaseインスタンスが使っている共有メモリーの容量から、適切なHugepagesのページ数を計算して出力します。

```
# sh hugepages_settings.sh
Recommended setting: vm.nr_hugepages = 1205
```

カーネルパラメーター設定後にOracle Databaseインスタンスを起動することで、インスタンスのSGAをHugepagesにマップすることができます。以下の例では全部で1,205ページ利用でき、15ページ余裕があります。

```
# grep Huge /proc/meminfo
AnonHugePages:         0 kB
HugePages_Total:    1205
HugePages_Free:       15
HugePages_Rsvd:       11
HugePages_Surp:        0
Hugepagesize:       2048 kB
```

# Hugepages関連のログ
## 正常時

SGAがHugepagesを使用したかどうかは、インスタンス起動時のalertログに出力されます。
下記は、正常にHugepagesが利用できた場合のログです。

```
**********************************************************************
2019-07-08T20:48:51.322293-04:00
Dump of system resources acquired for SHARED GLOBAL AREA (SGA)

2019-07-08T20:48:51.322496-04:00
 Per process system memlock (soft) limit = 7910M
2019-07-08T20:48:51.322604-04:00
 Expected per process system memlock (soft) limit to lock
 instance MAX SHARED GLOBAL AREA (SGA) into memory: 2402M
2019-07-08T20:48:51.322844-04:00
 Available system pagesizes:
  4K, 2048K
2019-07-08T20:48:51.323126-04:00
 Supported system pagesize(s):
2019-07-08T20:48:51.323232-04:00
  PAGESIZE  AVAILABLE_PAGES  EXPECTED_PAGES  ALLOCATED_PAGES  ERROR(s)
2019-07-08T20:48:51.323339-04:00
        4K       Configured               4               4        NONE
2019-07-08T20:48:51.323565-04:00
     2048K             1205            1201            1201        NONE
2019-07-08T20:48:51.323671-04:00
**********************************************************************
```

| ログ出力 | 説明 |
| :--- | :--- | 
| Per process system memlock (soft) limit | ユーザーのmemlock制限 |
| instance MAX SHARED GLOBAL AREA (SGA) into memory | インスタンスのSGA量 |
| Available system pagesizes: | サポートしているページのサイズ |
| Supported system pagesize(s) | ページサイズ毎の利用状況 |

## 不足時

初期化パラメーターuse_large_pagesがデフォルト値のTRUEになっている場合、Hugepagesの量が足りなくてもエラーにはならず、できる範囲でHugepagesから取得されます。その際には追加を推奨するログが出力されます。下記は、必要なHugepages 1201ページに対して600ページしか利用できない場合のログです。最終的に597ページが確保され、RECOMMENDATION 部分が追記されています。

```
 Supported system pagesize(s):
2019-07-08T21:39:55.054267-04:00
  PAGESIZE  AVAILABLE_PAGES  EXPECTED_PAGES  ALLOCATED_PAGES  ERROR(s)
2019-07-08T21:39:55.054375-04:00
        4K       Configured               4          309127        NONE
2019-07-08T21:39:55.054652-04:00
     2048K              600            1201             597        NONE
2019-07-08T21:39:55.054764-04:00
RECOMMENDATION:
2019-07-08T21:39:55.054870-04:00
 1. For optimal performance, configure system with expected number
 of pages for every supported system pagesize prior to the next
 instance restart operation.
2019-07-08T21:39:55.063104-04:00
**********************************************************************


```