Oracle DatabaseでのHugepages設計について

この記事は、 [JPOUG Advent Calendar 2024](https://adventar.org/calendars/10258) 18日目の記事です。
17日目は 「Noriyoshi Shinoda」 さんの [V$RMAN_OUTPUTビューを試す（Oracle Database 19c）](https://qiita.com/plusultra/items/b873a6b4820352071aad) という記事でした。

(2025/1/28追記)
2025/1/24 JPOUG Tech Talk Night #11 において、以下のイラスト加筆版でお話ししました。こちらのが分かりやすいと思います。
https://speakerdeck.com/sawakihideto/jpoug-tech-talk-night-number-11

## はじめに
Linux の Huge Page という用語をご存じでしょうか。大容量メモリを搭載するシステムのためのチューニング項目です。Oracle Database でも使用できますので、その概要、設定方法、見積方法、注意点をご紹介します。

（主に x86_64アーキテクチャの RHEL 8, Oracle Linux 8, Oracle Database 19c を対象に調べた内容により、アーキテクチャやバージョンにより当てはまらないこともあるためご注意ください）

## Huge Page とその効果について
Linux(x86_64アーキテクチャ)では物理メモリと仮想メモリのマッピング(ページテーブル)をデフォルトでは4KByteサイズのページ単位で管理します。マッピング1レコード分をページテーブルエントリ（以下PTE）と呼びますが、PTEもメモリ内に格納されます。
大容量メモリを搭載したシステムではこのPTEが大量に必要になることと、PTEをCPU内でキャッシュするTLB (Translation Lookaside Buffer) のキャッシュのミスが増えるオーバーヘッドによりパフォーマンスが低下します。
Huge Page という機能を用いることで、ページサイズを2MByte単位などに変更することができ、以下の効果が期待できます。
* メモリ使用量の減少
* パフォーマンスの向上

詳しく仕組みを知りたい方は、Redhat社 や Oracle社の該当ドキュメントを参照ください。
 
+ Redhat 社公式サイト 「RED HAT ENTERPRISE LINUX 8 システムのスループット、レイテンシー、および電力消費の最適化」[第36章 huge page の設定](https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/8/html/monitoring_and_managing_system_status_and_performance/configuring-huge-pages_monitoring-and-managing-system-status-and-performance "huge pageの設定")

+ Oracle 社公式サイト [大規模データベースのためのx86メモリ性能の設定方法 ](https://www.oracle.com/jp/technical-resources/articles/it-infrastructure/dev-hugepages.html "x86メモリ性能の設定方法")

ざっくりまとめると以下のような内容です。
+ SGA 8GBを4KB単位で管理するとPTEは200万超のレコード数にもなる
+ しかもOracleのSGAは共有メモリなので1プロセス毎（専用サーバ接続なら≒1セッション毎）にそれだけのPTEが必要になり、100プロセスなら100倍必要（この例ではページテーブル全体で766MBも占めている）
+ Huge Page を設定して2MB単位にすると、PTEのレコード数は512分の1になる
+ Huge Pageを使うと大容量メモリをアクセスする際にCPUが色んなところを参照する、という仕事が減る

## どう設定すれば良いか
大きく分けてLinuxとしての設定と、Oracle Database としての設定箇所があります。
+ Linuxとしての設定
  + カーネルパラメータ(/etc/sysctl.conf)に vm.nr_hugepages=value(ページ数) として利用したいHuge Pageのページ数を設定する
  + /etc/security/limits.confファイルの memlockを設定し、メモリ上にロックするアドレス空間のサイズをKBサイズで指定し引き上げる
  + 透過的な(Transparent)HugePagesを無効化する
(Huge Pageはアプリケーションが意識して割り当てないと利用できないですが、透過的なHugePagesはアプリケーションが意識しなくても利用できます　⇒　二つの機能を同時に使うことは様々な問題が発生するため推奨されていません）
  + OS再起動を行い、設定値が意図通り永続化されていることを確認する
  

+ Oracleとしての設定
  + 自動メモリー管理(AMM)を無効化する（Huge Pageとは同時には使えません）
  + 初期化パラメータuse_large_pagesを設定する

詳細な設定の仕方は以下を参照ください。  
Oracle 社公式サイト「データベース管理者リファレンス for Linux and UNIX System-Based Operating Systems」[A.7.4 LinuxでのHugePagesの構成](https://docs.oracle.com/cd/F19136_01/unxar/administering-oracle-database-on-linux.html#GUID-19BFCC60-BA4C-4750-85EB-7432282A9556)

## 必要なページ数(vm.nr_hugepages) の値はどう計算すれば良いか
先ほどの
[A.7.4 LinuxでのHugePagesの構成](https://docs.oracle.com/cd/F19136_01/unxar/administering-oracle-database-on-linux.html#GUID-19BFCC60-BA4C-4750-85EB-7432282A9556)に記載されているスクリプト(hugepages_settings.sh)を実行して必要な容量を計測します。(スクリプトは Oracle 社公式サイト[My Oracle Support | id=401749.1](https://support.oracle.com/epmos/faces/DocumentDisplay?id=401749.1)から取得します）
ざっくり説明すると、ipcs -m というコマンドの結果でOS全体で利用している(Oracle Databaseの利用かどうかに関わらない）共有メモリのサイズを合計し、2MByteのページサイズで割って（＋最低限必要なわずかなページを足して）ページ数を計算しています。

## 注意点その１
huge pageとして予約したページ分のメモリ(grep Huge /proc/meminfoの HugePages_Total: 行で確認可能）は、通常のページとしては利用できません。使いもしない分を過剰に予約しないようにしましょう。

## 注意点その２
Oracle 社公式サイト [My Oracle Support (https://support.oracle.com/epmos/faces/DocumentDisplay?id=401749.1)](https://support.oracle.com/epmos/faces/DocumentDisplay?id=401749.1)より引用
>ノート:このスクリプトを実行する前に、hugepagesを使用するすべてのアプリケーションが実行中であることを確認します。

とあるように、実際に Huge Page を使う予定のプロセスが全て実行中であることが重要です。
例えば稼働する予定の Oracle インスタンスは全て稼働させた状態とします。
（例：2つのOSでクラスタを組み、1OS内に1インスタンスずつ稼働系を負荷分散させている場合は、1OSに全てのインスタンスを片寄せして稼働させた状態にする）

## 注意点その３
必要量を算出する際は、Oracle Database 以外で Huge Page を使う予定のプロセスも全て実行させましょう。
以下のような事例が発生したことがあります。
+ 全Oracleインスタンスを起動させてスクリプトで必要Huge Page数を算出し設定
+ その後、OS再起動を実施するも、Huge Page 不足で Oracle インスタンスが起動しない

### 直接原因
原因を調査した結果、直接原因は以下の通りでした。
+ OS再起動時にOracle以外(JavaVM)のプロセスが先に起動しHuge Pageを確保
+ Oracleインスタンス起動時に必要 Huge Page 数が確保できず Oracle インスタンスが起動しない(初期化パラメータ use_large_pages が only に設定されている場合は Huge Page の不足時には起動しない)

### なぜ再起動するまで気づかなかったのか
+ 必要Huge Pageを算出する際には該当プロセス(JavaVM)を起動させていなかったため、見積りに含まれていなかった
+ また、後でJavaVMを起動させた際は「あればHuge Pageを使うし、なければ通常ページサイズにフォールバック」という動きをしたためエラーにならず気づかなかった
（参考：OpenJDK の開発に関わる Stefan Johansson さんの個人ページ[Large pages and Java](https://kstefanj.github.io/2021/05/19/large-pages-and-java.html) Java で Huge Page を使ったベンチマークも載っています。10%程度性能向上するらしいです。）

### どうすれば良かったのか
+ どのプロセスに Huge Page を使わせるかきちんと設計する
+ パフォーマンスを優先しない処理にはそもそも割り当てない（JavaVMに注意）

## 注意点その４
初期化パラメータ use_large_pages にはいくつかの設定値があります。デフォルト値で良いかどうかはきちんと設計しましょう。性能にこだわるならONLYが良いかと思います。Huge Page を使うのに ONLY 以外に設定するなら不足分がフォールバックした旨アラートログに出力されるので監視するようにしましょう。

Oracle社公式マニュアル [USE_LARGE_PAGES | Oracle 19c] (https://docs.oracle.com/cd/F19136_01/refrn/USE_LARGE_PAGES.html)

ざっくり以下のような理解です。
+ AUTO
    SGAに必要な Huge Page が足りなくても不足分はフォールバックして通常ページで確保されてインスタンスは起動する
+ FALSE
    Huge Page を使用しない
+ ONLY
    インスタンス起動前に既に OS で確保済みの Huge Page から SGA に必要なページ数を確保できなければインスタンスの起動に失敗する
+ AUTO_ONLY
    インスタンス起動時に Huge Page が不足していれば、OS の設定(vm.nr_hugepages）を動的に変更して確保し、それでも不足した場合はインスタンスの起動に失敗する（オンプレミスの Exadata の Oracle 19cのデフォルト）

Oracle社公式 [高可用性概要およびベスト・プラクティス | Oracle](https://docs.oracle.com/cd/F19136_01/haovw/oracle-database-configuration-best-practices.html#GUID-F23901EF-C6BD-46FD-B418-1E1CA9915D6F)
によると
>MAAのベスト・プラクティスはUSE_LARGE_PAGES=ONLYです。
> ノート：Exadataの USE_LARGE_PAGESの Oracle RDBMS 19c のデフォルトはAUTO_ONLYですが、この値は今後非推奨になります

とのことです。Exadata自動化ツールを使って設定すると ONLY になります。(19c)


RDS for Oracle の場合に選択できる設定は、以下を参照してください。
AWS公式 [サポートされている RDS for Oracle インスタンスで HugePages をオンにする](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Oracle.Concepts.HugePages.html)

## 参考文献
実際に Huge Page を設定を試してみる際は Oracle ACE Pro の Noriyoshi Shinoda さんの記事が分かりやすいです。
[Hugepages関連の設定を試す（Oracle Database 19c）](https://qiita.com/plusultra/items/bcfccb8ef1de46f1b9b7)