永続性が有効になっているKubernetesクラスターでIgniteを実行しています。各マシンにはJavaヒープ24GB、メモリ制限110GBの永続メモリ専用の20GBがあります。関連するJVMオプションは-XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC
です。すべてのノードでDataStreamerを数時間実行した後、クラスター上のノードがk8sのメモリ制限に達し、OOMキルがトリガーされました。Java NMTを実行した後、内部メモリに大量のスペースが割り当てられていることに驚きました。
Java Heap (reserved=25165824KB, committed=25165824KB)
(mmap: reserved=25165824KB, committed=25165824KB)
Internal (reserved=42425986KB, committed=42425986KB)
(malloc=42425954KB #614365)
(mmap: reserved=32KB, committed=32KB)
Kubernetesメトリックはこれを確認しました:
「IgniteCache」はカーネルページキャッシュです。最後のパネル「ヒープ+耐久性+バッファー」は、点火メトリックHeapMemoryUsed
+ PhysicalMemorySize
+ CheckpointBufferSize
の合計です。
DataStreamerはファイルを読み取るたびにフラッシュされ(最大約250MB)、一度に4つを超えるファイルを読み取るノードがないため、これはデータの蓄積の結果ではないことを私は知っていました。他の問題を除外した後、-XX:MaxDirectMemorySize=10G
を設定し、手動GCを呼び出してみましたが、すべてのポッドを定期的にシャットダウンして再起動する以外に影響はないようです。
ここからどこへ行けばいいのかわからない。 Igniteに、サードパーティのデータベースの使用を強制しない回避策はありますか?
編集:私のDataStorageConfiguration
<property name="dataStorageConfiguration">
<bean class="org.Apache.ignite.configuration.DataStorageConfiguration">
<property name="metricsEnabled" value="true"/>
<property name="checkpointFrequency" value="300000"/>
<property name="storagePath" value="/var/lib/ignite/data/db"/>
<property name="walFlushFrequency" value="10000"/>
<property name="walMode" value="LOG_ONLY"/>
<property name="walPath" value="/var/lib/ignite/data/wal"/>
<property name="walArchivePath" value="/var/lib/ignite/data/wal/archive"/>
<property name="walSegmentSize" value="2147483647"/>
<property name="maxWalArchiveSize" value="4294967294"/>
<property name="walCompactionEnabled" value="false"/>
<property name="writeThrottlingEnabled" value="False"/>
<property name="pageSize" value="4096"/>
<property name="defaultDataRegionConfiguration">
<bean class="org.Apache.ignite.configuration.DataRegionConfiguration">
<property name="persistenceEnabled" value="true"/>
<property name="checkpointPageBufferSize" value="2147483648"/>
<property name="name" value="Default_Region"/>
<property name="maxSize" value="21474836480"/>
<property name="metricsEnabled" value="true"/>
</bean>
</property>
</bean>
</property>
更新:永続性を無効にすると、内部メモリは適切に破棄されます:
更新:この問題は、再現可能な例で ここ に示されています。 Docker用に少なくとも22GBのメモリと約50GBのストレージを備えたマシンで実行できます。興味深いことに、リークは、バイト配列または文字列を値として渡す場合にのみ実際に目立ちます。
メモリリークは、IgniteでLuceneクエリをサポートするキャッシュモデルの値オブジェクトの_@QueryTextField
_アノテーションによってトリガーされているようです。
元々:case class Value(@(QueryTextField@field) theta: String)
この行を次のように変更すると、case class Value(theta: String)
が問題を解決するようです。これが機能する理由については説明がありませんが、Igniteコードベースをよく理解している人が理由を説明できるかもしれません。
walSegmentSize=64mb
を設定し(または設定を削除してデフォルトを使用し)、-XX:MaxDirectMemorySize=<walSegmentSize * 4>
を設定します。
Igniteのメモリニーズを計算するときに忘れがちなことの1つは、直接メモリバッファサイズです。
ダイレクトメモリバッファは、Javaプロセスの個別のスペースから割り当てられたJVM管理バッファです。これは、Javaヒープ、Igniteデータ領域、またはIgniteチェックポイントバッファではありません。
直接メモリバッファは、Javaの非ヒープメモリと対話する通常の方法です。 (JVMの内部コードからアプリケーションまで)それを使用するものはたくさんありますが、Igniteサーバーでは、直接メモリプールの主なユーザーは先行書き込みログです。
デフォルトでは、Igniteはメモリマップトファイルを使用してWALに書き込みます。これは、直接メモリバッファを介して機能します。そのバッファのサイズは、WALセグメントのサイズです。そして、ここで私たちは楽しいものに行き着きます。
あなたのWALセグメントは巨大です! 2GB-それはたくさんです。デフォルトは64MBで、それ以上を使用する環境はめったに見たことがありません。一部の特定のワークロードおよび一部の特定のディスクでは、256MBを設定することをお勧めします。
したがって、直接メモリプールに作成されている2GBのバッファがあります。デフォルトでは、ダイレクトメモリの最大サイズは-Xmx
に等しく、この場合は24GBです。直接メモリプールが(まだクリアされていない古いバッファから)24GBに膨れ上がり、アプリケーションの合計サイズが少なくとも20 + 2 + 24 + 24 = 70GB
!になるシナリオを見ることができます。
これは、40GBの内部JVMメモリを説明しています(これはデータ領域+直接だと思います)。これは、永続性がオフのときに問題が発生しない理由も説明しています。その場合、WALはありません。
正気のwalSegmentSize
を選択してください。 2GBを選択した理由はわかりませんが、小さなWALセグメントで問題が発生したことが確実な場合は、デフォルトの64MBまたは256MBのいずれかを選択することをお勧めします。
-XX:MaxDirectMemorySize=<size>
を介してJVMの直接メモリプールに制限を設定します。 walSegmentSize * 4
の値、つまり256mb〜1gbの範囲に設定するのが安全な選択だと思います。
上記の変更を行った後にメモリ消費の問題が発生した場合でも、99%のクラスターに最適であるという理由だけで、とにかくそれらを保持してください。
あなたの場合の「内部」が何であるかはわかりませんが、Igniteは通常すべてのデータをオフヒープメモリに保存します。 '直接'メモリでもないことに注意してください。
オフヒープ専用のメモリ量を構成する 、および ページエビクションを構成する を実行できます。
永続性を有効にした場合と無効にした場合で、グラフからのignite-cacheメトリックに大きなギャップが見られます。つまり、永続性を使用すると、実際にはデータストレージディレクトリwal、walArchiveにデータを書き込んでいます。 Kubernetesポッドがそのディレクトリのメモリ制限も考慮している場合は、すぐにメモリが不足する可能性があります。