FE / コーディネーターノードのメモリ不足問題のトラブルシューティング
「FEメモリ不足」の問題を防ぎ、迅速に回復し、再発防止のためにできる限り原因を特定します。
- この情報はFEノードおよびコーディネーターノード(共有データデプロイメント)に適用されます。読みやすさのために、両方のタイプのフロントエンドノードを「FE」と呼びます。
- この記事では主にヒープメモリの状況と解決策を分析しており、オフヒープメモリについては扱いません。OOMがオフヒープメモリの問題によって引き起こされている場合は、JVM XMXの設定を減らすか、マシンのメモリを拡張することを検討してください。
FEメモリ構造と割り当ての推奨事項
FEメモリの構成
フロントエンドサービス(FE)はJVM上で動作し、そのメモリ消費はJavaヒープとオフヒープメモリのオーバーヘッドから成ります。
JVMメモリモジュールの概要
FEプロセス中にJVMを通じて割り当てられるすべてのメモリは、大まかに以下のモジュールに分けられます:
| モジュール | タイプ | 説明 |
|---|---|---|
| Javaヒープ | ヒープ | Javaオブジェクトインスタンスのメモリ(プランキャッシュ、メタデータ、一時オブジェクトなど) |
| クラス | オフヒープ | クラスのロード時に生成されるメタデータなどの構造 |
| スレッド | オフヒープ | スレッドが占有するメモリは主に各スレッドのスタック空間です(通常、スレッドあたり約1MB)。 |
| コード | オフヒープ | JVMがバイトコードをネイティブマシンコードにコンパイルする際に占有するメモリ |
| GC | オフヒープ | ガベージコレクターの内部作業データ構造 |
| コンパイラ | オフヒープ | HotSpotコンパイラが使用するメモリはCodeと同程度になる場合があります。 |
| その他 | オフヒープ | Direct ByteBuffer、Nettyバッファなどのダイレクトメモリを含む |
| シンボル | オフヒープ | 文字列定数プール、整数定数プールなどのSymbolTable構造 |
| ネイティブメモリトラッキング(NMT) | オフヒープ | NMT自身が記録するメモリ使用量が占有するメモリ |
| アリーナチャンク | オフヒープ | JVMが一時的な保存と再利用のために内部で使用する一時メモリブロック(mallocを使用して割り当て) |
| ロギング | オフヒープ | ロギングシステム(log4jなど)が占有するメモリ |
| 引数 | オフヒープ | JVM起動パラメーターの受け渡しが占有するメモリ |
| モジュール | オフヒープ | Javaモジュールシステムで使用される構造 |
JVMおよびStarRocksの動作特性を考慮すると、FEのメモリは主に以下の部分で構成されます:
-
JVMヒープメモリ(ヒープ)
- Javaオブジェクトインスタンス(Plan Cache、メタデータキャッシュなど)を格納します。
- 最大サイズはパラメータ
-Xmxで設定され、ガベージコレクションメカニズムに大きく影響されます。
-
非ヒープメモリ(Metaspace)
- ストレージクラス定義、定数プールなどのメタデータ。
- Java 8以降でPermGenに置き換わりました。
-
ダイレクトメモリ
- Netty、ByteBuffer、ログバッファ、RPCフレームワークなどが使用するオフヒープメモリ。
sun.misc.Unsafeによって割り当てられ、通常はGCによって制御されません。
-
スレッドスタックメモリ
- 各Javaスレッドは作成時にスレッドスタックを要求し、デフォルトサイズは約1MB(
-Xssで設定可能)です。 - スレッド数が多い場合(高並列シナリオなど)、メモリの積み上がりが発生しやすくなります。
- 各Javaスレッドは作成時にスレッドスタックを要求し、デフォルトサイズは約1MB(
-
JNI / ネイティブメモリ呼び出し
- データベースは、ロギングコンポーネント(RocksDB、Nettyなど)、Cacheなどを通じてネイティブレイヤーを呼び出す際にメモリを消費する場合があります。
JVMパラメータ設定の推奨事項
JVMでは、ヒープメモリは Xmx で設定します。一般的に、プロセスが使用するメモリが Xmx の130%未満であることが適切です。つまり、JVM Xmx で設定されたメモリに加えて、一部のオフヒープメモリも使用されます。例えば、JVMに21gを設定した場合、top で確認できるプロセスのRSSメモリ使用量は約27gになります。
経験に基づくと、タブレット数とFEメモリの推奨関係は以下の通りです:
| タブレット数 | 推奨FEメモリ設定 |
|---|---|
| 1M未満 | 16 GB |
| 1M - 2M | 32 GB |
| 2M - 5M | 64 GB |
| 5M - 10M | 128 GB |
独立デプロイ環境では、システムに十分なメモリを確保することを推奨します。システム用に確保するメモリに加えて、オフヒープメモリも考慮する必要があります。
混合デプロイ環境では、OOMを防ぐために、他のサービスの使用状況を考慮した上で、できる限り十分なメモリを確保する必要があります。
FEを独立デプロイする場合(混合デプロイは推奨しません):
- マシンのメモリが32G未満の場合、
-Xmx(最大ヒープサイズ)はマシンメモリの最大70%に設定してください。 - マシンのメモリが32Gを超える場合、
-Xmx(最大ヒープサイズ)はマシンメモリの最大80%に設定してください。 - FEリーダーノードはチェックポイントを実行する必要があるため、一般的にリーダーノードのメモリはフォロワーノードの約2倍になります。
バージョン3.4以降では、リーダーはフォロワーノードを選択してチェックポイントを実行し、フォロワーの結果をダウンロードして他のフォロワーに配布します。リーダーのメモリ使用量はフォロワーに近くなります。参照:PR #52103。
FE JVMモニタリングとメモリプロファイルのデプロイ
FEの状態において、ヒープモニタリングとGCモニタリングはサービスの状態を効果的に監視でき、事後の問題解決における重要な手がかりにもなります。タイムリーにモニタリングを設定し、必要に応じて対応するアラートを設定することを推奨します。
具体的なモニタリング指標は以下の通りです:
| 指標 | 説明 |
|---|---|
jvm_heap_size_bytes | ヒープメモリ使用量。 |
jvm_non_heap_size_bytes | 主にmetaspace、コードキャッシュなどを含み、すべてのオフヒープメモリは含みません。 |
jvm_old_gc{type="count"} | Full GC回数。 |
jvm_old_gc{type="time"} | Full GC時間。 |
これらのメトリクスおよびその他のモニタリング情報はモニタリングとアラート
メモリプロファイルは突発的なヒープ増加を分析できます。サービスのインストール完了後、正常に動作するかどうかを確認する必要があります。
3.3.6以降では、データベースは定期的にメモリプロファイルを出力し、log/proc_profile にHTML形式でtgz圧縮して保存します。
制御設定:
proc_profile_collect_interval_s = 600
proc_profile_collect_time_s = 300
proc_profile_cpu_enable = true
proc_profile_mem_enable = true
3.3.6より前のバージョンでは、スクリプトを使用して定期的にプロファイルを出力できます。各FEのインストールディレクトリ配下に mem_profiler.sh という名前の新しいスクリプトファイルを作成し、以下の内容をファイルにコピーしてください。
このスクリプトはFEプロセスに影響を与えず、サービスの通常使用に影響しません。また、48時間経過したファイルを自動的にクリーンアップします。
#!/bin/bash
mkdir -p mem_alloc_log
cleanup_old_files() {
find mem_alloc_log -name "alloc-profile-*.html" -mmin +2880 -exec rm -f {} \;
}
while true
do
cleanup_old_files
current_time=$(date +'%Y-%m-%d-%H-%M-%S')
file_name="mem_alloc_log/alloc-profile-${current_time}.html"
./bin/profiler.sh -e alloc --alloc 2m -d 300 -f "$file_name" `cat bin/fe.pid`
done
# バックグラウンド起動
chmod +x mem_profiler.sh
nohup ./mem_profiler.sh > mem_profiler.out 2>&1 &
# プロセスが存在するか確認する
ps aux | grep mem_profiler.sh
# プロセスを停止する
pkill -f mem_profiler.sh
FEクラッシュ
FEクラッシュの原因は大きく4種類に分けられます。
1. FEプロセスのメモリ使用量が高く、オペレーティングシステムによって強制終了された
システムログを照会することで、これが原因かどうかを確認できます:
# dmesgを確認する
dmesg | grep -iE 'out of memory|oom|kill|killed process'
# メッセージログを表示する
sudo grep -Ei 'killed process|oom.kill|sending SIG' -C5 /var/log/messages /var/log/syslog 2>/dev/null | tail -n 100
OOMによって強制終了された後、ログにはシステムプロセスのメモリ使用量が出力される場合があります。OOMによって表示されるtotal_vmやrssなどのフィールドはページ単位であり、デフォルトでは1ページ = 4KBです:
total_vm = 8793525→ 約8793525 × 4KB ≈ 33.5 GBの仮想メモリを要求したことを示しますrss = 7218120→ 約27.5 GBの物理メモリが実際に使用されており、それは7218120 × 4KBであることを示します
OOMログの例:
Jun 10 11:20:24 tp-prod-bigdata-sr-ss-fe-2-b kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
Jun 10 11:21:58 tp-prod-bigdata-sr-ss-fe-2-b kernel: [22654] 1005 22654 8793525 7218120 14943 0 0 java
実際のRSS使用量を確認してください。マシンのメモリに非常に近い場合、一般的にJVMの割り当てが不適切であることが原因です。JVMパラメータ設定の推奨事項セクションを参照してください。
2. FEリーダーのヒープメモリが高く、Full GCがトリガーされ、リーダーの切り替えが発生した
リーダーの切り替えにより旧リーダーが終了すると、fe.outに以下の情報が出力されます:
transfer FE type from LEADER to UNKNOWN. exit
または
transfer FE type from LEADER to FOLLOWER. exit
ほとんどのリーダー切り替えはFull GCによって引き起こされます。まれに、リーダーのCPU使用率が高い場合やjstackの実行によってトリガーされる場合があります。
GCログの分析:
GCログをhttps://gceasy.io/にアップロードし、ポーズGCの時間をクリックしてください。単一のポーズGCの時間が30秒を超える場合、または10秒を超えるポーズGCが頻繁に発生する場合、リーダーの切り替えがトリガーされます。
をクリックしてGC前のヒープヒープメモリが上昇する時点を見つけてください。この時間範囲を使用して、その期間のメモリプロファイルファイルを特定し、その時点でどのモジュールがメモリを要求していたかを識別します。