メインコンテンツまでスキップ
バージョン: 2.1

Jemalloc メモリ解析

DorisはデフォルトでJemallocを汎用メモリアロケーターとして使用します。Jemalloc自体が占有するメモリには、CacheとMetadataが含まれます。CacheにはThread CacheとDirty Pageが含まれます。メモリアロケーターの元のプロファイルは、http://{be_host}:{be_web_server_port}/memzでリアルタイムに確認できます。

Jemalloc Cache メモリ分析

Label=tc/jemalloc_cache, タイプ=overviewのMemory Trakkerの値が大きい場合、JemallocまたはTCMalloc Cacheが大量のメモリを使用していることを意味します。DorisはデフォルトのAllocatorとしてJemallocを使用するため、ここではJemalloc Cacheが大量のメモリを使用する状況のみを分析します。

MemTrackerLimiter Label=tc/jemalloc_cache, タイプ=overview, Limit=-1.00 B(-1 B), Used=410.44 MB(430376896 B), Peak=-1.00 B(-1 B)

Doris 2.1.6以前では、Label=tc/jemalloc_cacheにはJemalloc Metadataも含まれており、Jemalloc Metadataの大きなメモリ使用量がLabel=tc/jemalloc_cacheを過大にしている可能性があります。Label=tc/jemalloc_metadata Memory Trackerの分析を参照してください。

BEプロセスの実行中、Jemalloc Cacheは2つの部分から構成されます。

  • Thread Cache、Thread Cache内で指定された数のPagesをキャッシュします。Jemalloc opt.tcacheを参照してください。

  • Dirty Page、Arena内で再利用可能なすべてのメモリPages。

Jemalloc Cache View方法

Doris BEのWebページhttp://{be_host}:{be_web_server_port}/memz(webserver_portのデフォルトは8040)を表示してJemalloc Profileを取得し、いくつかの重要な情報に基づいてJemalloc Cacheの使用状況を解釈します。

  • Jemalloc Profileのtcache_bytesは、Jemalloc Thread Cacheの総バイト数です。tcache_bytesの値が大きい場合、Jemalloc Thread Cacheが使用するメモリが過大であることを意味します。

  • Jemalloc ProfileのextentsTableのdirty列の値の合計が大きい場合、Jemalloc Dirty Pageが使用するメモリが過大であることを示します。

Thread Cacheメモリが過大な場合

Thread Cacheが大量の大きなページをキャッシュしている可能性があります。これは、Thread Cacheの上限がページ数であり、ページの総バイト数ではないためです。

be.confJEMALLOC_CONFlg_tcache_maxを削減することを検討してください。lg_tcache_maxは、キャッシュが許可されるページのバイトサイズの上限です。デフォルト値は15、つまり32 KB(2^15)です。このサイズを超えるページはThread Cacheにキャッシュされません。lg_tcache_maxは、Jemalloc ProfileのMaximum thread-cached size classに対応します。

Doris 2.1以前では、be.confJEMALLOC_CONFにおけるlg_tcache_maxのデフォルト値は20であり、一部のシナリオでJemalloc Cacheが過大になる原因となっていました。Doris 2.1以降、Jemallocのデフォルト値である15に変更されています。

これは通常、BEプロセス内のクエリやロードが大量の大きなサイズクラスのメモリページを申請しているか、または大きなメモリクエリやロードの実行後に、大量の大きなサイズクラスのメモリページがThread Cacheにキャッシュされているためです。Thread Cacheをクリーンアップするタイミングは2つあります。1つは、メモリの申請と解放が一定回数に達した際に長時間使用されていないメモリブロックを回収する場合、もう1つは、スレッドが終了する際にすべてのページを回収する場合です。この時、Bad Caseがあります。スレッドが将来新しいクエリやロードを実行しない場合、メモリを割り当てることがなくなり、いわゆるidle状態に陥ります。ユーザーはクエリ完了後にメモリが解放されることを期待しますが、実際には、このシナリオでは、スレッドが終了しない限り、Thread Cacheはクリーンアップされません。

ただし、通常Thread Cacheに注意を払う必要はありません。プロセスの利用可能メモリが不足している場合、Thread Cacheのサイズが1Gを超えると、DorisはThread Cacheを手動でフラッシュします。

Dirty Pageメモリが過大な場合

extents:        size ind       ndirty        dirty       nmuzzy        muzzy    nretained     retained       ntotal        total
4096 0 7 28672 1 4096 21 86016 29 118784
8192 1 11 90112 2 16384 11 90112 24 196608
12288 2 2 24576 4 49152 45 552960 51 626688
16384 3 0 0 1 16384 6 98304 7 114688
20480 4 0 0 1 20480 5 102400 6 122880
24576 5 0 0 43 1056768 2 49152 45 1105920
28672 6 0 0 0 0 13 372736 13 372736
32768 7 0 0 1 32768 13 425984 14 458752
40960 8 0 0 31 1150976 35 1302528 66 2453504
49152 9 4 196608 2 98304 3 139264 9 434176
57344 10 0 0 1 57344 9 512000 10 569344
65536 11 3 184320 0 0 6 385024 9 569344
81920 12 2 147456 3 241664 38 2809856 43 3198976
98304 13 0 0 1 86016 6 557056 7 643072
114688 14 1 102400 1 106496 15 1642496 17 185139

be.confJEMALLOC_CONFdirty_decay_msを2000ms以下に減らしてください。be.confのデフォルトのdirty_decay_msは5000msです。Jemallocはdirty_decay_msで指定された時間内に、滑らかなグラデーション曲線に従ってダーティページを解放します。参考:Jemalloc opt.dirty_decay_ms。BEプロセスで利用可能メモリが不足してMinor GCまたはFull GCがトリガーされると、特定の戦略に従ってすべてのダーティページを積極的に解放します。

Doris 2.1より前では、be.confJEMALLOC_CONFdirty_decay_msのデフォルト値は15000でしたが、これは一部のシナリオでJemalloc Cacheが大きくなりすぎる原因となっていました。Doris 2.1以降、デフォルト値は5000になっています。

Jemalloc Profileのextentsには、すべてのJemalloc arenaの異なるページサイズのバケットの統計値が含まれており、ndirtyはダーティページの数、dirtyはダーティページの総メモリです。Jemallocstats.arenas.<i>.extents.<j>.{extent_type}_bytesを参照し、すべてのPage Sizesのdirtyを合計すると、JemallocのDirty Pageのメモリバイトサイズを取得できます。

Jemalloc Metadataメモリ分析

Label=tc/jemalloc_metadata, タイプ=overviewのMemory Trakcer値が大きい場合、JemallocまたはTCMalloc Metadataが大量のメモリを使用していることを意味します。DorisではJemallocをデフォルトのAllocatorとして使用しているため、ここではJemalloc Metadataが大量のメモリを使用している状況のみを分析します。

MemTrackerLimiter Label=tc/jemalloc_metadata, タイプ=overview, Limit=-1.00 B(-1 B), Used=144 MB(151759440 B), Peak=-1.00 B(-1 B)

Label=tc/jemalloc_metadata Memory Trackerは、Doris 2.1.6以降に追加されました。以前は、Jemalloc MetadataはLabel=tc/jemalloc_cache Memory Trackerに含まれていました。

Jemalloc Metadataの確認方法

Doris BEのWebページhttp://{be_host}:{be_web_server_port}/memz(webserver_portのデフォルトは8040)を表示することで、Jemalloc Profileを取得できます。Jemalloc Profileで以下のようなJemallocの全体的なメモリ統計を確認してください。ここでmetadataはJemalloc Metadataのメモリサイズです。

Allocated: 2401232080, active: 2526302208, metadata: 535979296 (n_thp 221), resident: 2995621888, mapped: 3221979136, retained: 131542581248

  • Allocated JemallocがBEプロセスのために割り当てたメモリの総バイト数。

  • active JemallocがBEプロセスのために割り当てた全ページの総バイト数で、Page Sizeの倍数であり、通常Allocated以上になります。

  • metadata Jemallocメタデータの総バイト数で、割り当ておよびキャッシュされたページ数、メモリ断片化などの要因に関連します。ドキュメントJemalloc stats.metadataを参照してください。

  • retained Jemallocが保持する仮想メモリマッピングのサイズで、munmapや類似の方法を通じてオペレーティングシステムに返されておらず、物理メモリと強く関連していません。参考ドキュメントJemalloc stats.retained

Jemalloc Metadataメモリが大きすぎる場合

Jemalloc Metadataのサイズは、プロセスの仮想メモリサイズと正の相関があります。通常、Doris BEプロセスの仮想メモリが大きいのは、Jemallocが大量の仮想メモリマッピング、つまり上記のretainedを保持しているためです。Jemallocに返された仮想メモリは、デフォルトでRetainedにキャッシュされ、再利用されるのを待っており、自動的または手動的に解放されることはありません。

Jemalloc Retainedのサイズが大きい根本的な理由は、Dorisコードレベルでのメモリ再利用が不十分で、大量の仮想メモリを申請する必要があり、解放後にJemalloc Retainedに入るためです。通常、仮想メモリとJemalloc Metadataサイズの比率は300-500の間で、つまり10Tの仮想メモリがある場合、Jemalloc Metadataは20Gを占有する可能性があります。

Jemalloc MetadataとRetainedが継続的に増加し、プロセスの仮想メモリが大きすぎる問題が発生した場合は、定期的にDoris BEプロセスを再起動することを検討することをお勧めします。通常、これはDoris BEが長時間実行された後にのみ発生し、少数のDorisクラスターのみが遭遇します。現在、パフォーマンスを失うことなく、Jemalloc Retainedが保持する仮想メモリマッピングを削減する方法はありません。Dorisは継続的にメモリ使用量を最適化しています。

上記の問題が頻繁に発生する場合は、以下の方法を参照してください。

  1. 根本的な解決策は、Jemalloc Retainedキャッシュ仮想メモリマッピングを無効にすることです。be.confJEMALLOC_CONFの後にretain:falseを追加し、BEを再起動してください。ただし、クエリパフォーマンスが大幅に低下する可能性があり、TPC-H Benchmarkテストのパフォーマンスは約3倍低下します。

  2. Doris 2.1では、set global experimental_enable_pipeline_engine=false; set global experimental_enable_pipeline_x_engine=false;を実行してPipelinexとPipelineを無効にできます。これは、pipelinexとpipelineがより多くの仮想メモリを申請するためです。これもクエリパフォーマンスの低下につながります。