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_metadataMemory 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.confのJEMALLOC_CONFでlg_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.confのJEMALLOC_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.confのJEMALLOC_CONFのdirty_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.confのJEMALLOC_CONFのdirty_decay_msのデフォルト値は15000でしたが、これは一部のシナリオでJemalloc Cacheが大きくなりすぎる原因となっていました。Doris 2.1以降、デフォルト値は5000になっています。
Jemalloc Profileのextentsには、すべてのJemalloc arenaの異なるページサイズのバケットの統計値が含まれており、ndirtyはダーティページの数、dirtyはダーティページの総メモリです。Jemallocのstats.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_metadataMemory Trackerは、Doris 2.1.6以降に追加されました。以前は、Jemalloc MetadataはLabel=tc/jemalloc_cacheMemory 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
-
AllocatedJemallocがBEプロセスのために割り当てたメモリの総バイト数。 -
activeJemallocがBEプロセスのために割り当てた全ページの総バイト数で、Page Sizeの倍数であり、通常Allocated以上になります。 -
metadataJemallocメタデータの総バイト数で、割り当ておよびキャッシュされたページ数、メモリ断片化などの要因に関連します。ドキュメントJemalloc stats.metadataを参照してください。 -
retainedJemallocが保持する仮想メモリマッピングのサイズで、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は継続的にメモリ使用量を最適化しています。
上記の問題が頻繁に発生する場合は、以下の方法を参照してください。
-
根本的な解決策は、Jemalloc Retainedキャッシュ仮想メモリマッピングを無効にすることです。
be.confのJEMALLOC_CONFの後にretain:falseを追加し、BEを再起動してください。ただし、クエリパフォーマンスが大幅に低下する可能性があり、TPC-H Benchmarkテストのパフォーマンスは約3倍低下します。 -
Doris 2.1では、
set global experimental_enable_pipeline_engine=false; set global experimental_enable_pipeline_x_engine=false;を実行してPipelinexとPipelineを無効にできます。これは、pipelinexとpipelineがより多くの仮想メモリを申請するためです。これもクエリパフォーマンスの低下につながります。