Elasticsearch JVM-Heap-Größe verstehen und für die Leistung optimieren

Praktische Anleitung zur Dimensionierung des Elasticsearch JVM-Heaps, zum Erkennen von GC-Symptomen und zur Vermeidung von Speichereinstellungen, die die Suchleistung beeinträchtigen.

Elasticsearch JVM-Heap-Größe verstehen und für die Leistung optimieren

Die Elasticsearch JVM-Heap-Größe ist eine dieser Einstellungen, die oft zu früh angefasst und dann zu spät bemängelt werden. Ein langsamer Cluster braucht nicht immer mehr Heap. Manchmal ist das Gegenteil der Fall: Der Heap ist groß genug, aber dem Betriebssystem bleibt zu wenig Arbeitsspeicher für den Dateisystem-Cache, sodass Lucene häufiger auf die Festplatte zugreifen muss. In anderen Fällen ist der Heap tatsächlich zu klein, die Garbage Collection läuft ständig und jede Suche fühlt sich an, als würde man durch nassen Zement waten.

Das praktische Ziel ist es nicht, eine magische Zahl zu finden. Das Ziel ist es, Elasticsearch genügend Heap für Cluster-Metadaten, Indexpuffer, Aggregationen, Abfragearbeit und Caches zu geben, während außerhalb des Heaps genügend RAM für Lucene-Segmentdateien und das Betriebssystem übrig bleibt. Wenn Sie sich nur an eines erinnern, dann daran, dass die Elasticsearch-Leistung sowohl vom Heap als auch vom Off-Heap-Speicher abhängt.

Beginnen Sie damit, zwei Arten von Speicherdruck zu unterscheiden. JVM-Heap-Druck zeigt sich als hoher heap.percent, lange Garbage-Collection-Pausen, Parent-Circuit-Breaker-Ausnahmen, Fielddata-Druck oder OutOfMemoryError. Off-Heap-Druck äußert sich normalerweise in langsamen Suchen, obwohl der Heap in Ordnung zu sein scheint, hohen Festplattenlesevorgängen, Swap-Aktivität oder schlechten Cache-Trefferquoten nach einem Neustart. Die Erhöhung des Heaps kann im ersten Fall helfen. Im zweiten Fall kann sie die Situation verschlimmern.

Für die meisten selbstverwalteten Cluster funktioniert die alte Faustregel immer noch als Ausgangspunkt: Setzen Sie den Heap auf nicht mehr als die Hälfte des Arbeitsspeichers der Maschine und halten Sie ihn unterhalb der Schwelle für komprimierte gewöhnliche Objektzeiger. Diese Schwelle wird oft mit "etwa 32 GB" angegeben. In der Praxis bewegen sich viele Betreiber im Bereich von 26-31 GB, da der genaue Grenzwert von der JVM und dem Laufzeitlayout abhängt. Elasticsearch protokolliert beim Start, ob komprimierte gewöhnliche Objektzeiger aktiviert sind. Behandeln Sie das Startprotokoll als die maßgebliche Quelle für Ihren Knoten.

In modernen Elasticsearch-Versionen kann die automatische Heap-Größenanpassung bereits einen angemessenen Wert basierend auf den Knotenrollen und dem verfügbaren Arbeitsspeicher festlegen. Das ist nützlich, besonders für kleinere Cluster und Standardbereitstellungen. Manuelles Tuning ist immer noch wichtig, wenn ein Knoten eine ungewöhnliche Arbeitslast hat: starke Aggregationen, große Mappings, viele Shards, Ingest-Pipelines, Transform-Jobs, Machine-Learning-Rollen oder eine Mischung aus intensiver Suche und hohem Indexierungsvolumen.

Hier ist ein einfaches Beispiel. Angenommen, Sie haben einen 64 GB heißen Datenknoten, der sowohl Indexierung als auch Suche bewältigt. Ein Heap von 30 GB ist ein üblicher Ausgangspunkt. Es lässt etwa die Hälfte des RAMs für den OS-Page-Cache und nativen Speicher übrig. Wenn derselbe Knoten Teil eines Logging-Clusters mit vielen kleinen Shards und Aggregationen mit hoher Kardinalität ist, könnten Sie immer noch Heap-Druck sehen. Die Lösung könnte ein besseres Shard-Design oder eine Mapping-Bereinigung sein, nicht automatisch ein 40-GB-Heap. Das Überschreiten der Schwelle für komprimierte Zeiger kann die Objektzeigergröße erhöhen und die effektive Heapeffizienz verringern.

Setzen Sie den minimalen und maximalen Heap auf denselben Wert. Elasticsearch sollte keine Zeit damit verbringen, den Heap während des Betriebs zu vergrößern oder zu verkleinern.

-Xms30g
-Xmx30g

Verwenden Sie eine Datei unter jvm.options.d/, anstatt die mitgelieferte jvm.options-Datei direkt zu bearbeiten, wenn Ihre Installation dies unterstützt. Bei Paketinstallationen bedeutet das normalerweise etwas wie /etc/elasticsearch/jvm.options.d/heap.options. Für Docker übergeben Sie die Heap-Einstellungen über die unterstützte Umgebungsvariable oder eine eingehängte Konfiguration. Halten Sie die Einstellung für Knoten mit derselben Rolle konsistent, aber gehen Sie nicht davon aus, dass jede Rolle denselben Heap benötigt. Dedizierte master-fähige Knoten benötigen oft viel weniger Heap als stark ausgelastete Datenknoten, es sei denn, der Cluster hat aufgrund zu vieler Indizes, Felder oder Shards sehr große Metadaten.

Bevor Sie den Heap ändern, machen Sie eine Momentaufnahme des aktuellen Verhaltens. Sehen Sie sich die JVM-Statistiken an:

GET _nodes/stats/jvm?filter_path=nodes.*.jvm.mem,nodes.*.jvm.gc

Überprüfen Sie dann die Breaker und Fielddata:

GET _nodes/stats/breaker,indices/fielddata?pretty

Überprüfen Sie auch Shards und Mappings. Ein Cluster mit Tausenden von winzigen Shards kann Heap für Overhead verbrauchen, selbst wenn das Datenvolumen nicht riesig ist.

GET _cat/shards?v&bytes=gb
GET _cluster/stats?filter_path=indices.count,indices.shards,indices.mappings

Die Symptome sind wichtiger als die reine Zahl. Ein Heap, der während Lastspitzen bei etwa 70-85 % liegt, kann normal sein, wenn die Garbage Collection ihn schnell wieder senkt. Ein Heap, der in den hohen 90ern klettert und dort bleibt, ist etwas anderes. Lange GC-Pausen der alten Generation sind schlimmer als ein hoher Prozentsatz für sich allein. Wenn Suchvorgänge jedes Mal ausfallen, wenn die GC läuft, ist es den Benutzern egal, dass die durchschnittliche Heap-Auslastung im Diagramm akzeptabel aussah.

Ein häufiger Produktionsfehler ist die Verwendung des Heaps als Pflaster für schlechte Mappings. Das Sortieren oder Aggregieren von analysierten text-Feldern mit fielddata: true kann viel Heap verbrauchen. Die bessere Lösung ist normalerweise ein keyword-Unterfeld, ein Feld mit niedrigerer Kardinalität oder ein anderes Aggregationsdesign. Ein weiterer Fehler besteht darin, dynamischen Mappings zu erlauben, Tausende von Feldern aus beliebigen JSON-Schlüsseln zu erstellen. Heap verschwindet dann in Mappings, Clusterzustand und Abfragestrukturen. Setzen Sie Grenzen für dynamische Felder, bevor sie zu einem betrieblichen Problem werden.

Aggregationen verdienen besondere Aufmerksamkeit. Eine Terms-Aggregation für ein Feld mit hoher Kardinalität kann große speicherinterne Strukturen erzeugen. Wenn jemand ein Dashboard ausführt, das nach user_id, session_id oder vollständiger URL über einen langen Zeitraum gruppiert, kann der Heap-Druck selbst dann ansteigen, wenn normale Suchvorgänge in Ordnung aussehen. Verwenden Sie kleinere Zeitfenster, Filter, die die Arbeitsmenge eingrenzen, composite-Aggregation für die Paginierung oder gegebenenfalls voraggregierte Rollups. Eine Erhöhung des Heaps kann den Fehler hinauszögern, macht eine unbegrenzte Aggregation aber nicht günstig.

Die Indexierung hat ihr eigenes Heap-Muster. Bulk-Anfragen erzeugen vorübergehenden Druck. Sehr große Bulk-Payloads können Elasticsearch dazu zwingen, zu viel Arbeit auf einmal zu halten. Wenn Sie Indexierungsablehnungen oder Heap-Spitzen während der Erfassung sehen, versuchen Sie es mit kleineren Bulk-Batches und mehr clientseitiger Parallelitätskontrolle. Ein nützlicher Ausgangsbereich ist oft ein paar Megabyte pro Bulk-Anfrage, dann testen Sie mit Ihren echten Dokumenten nach oben. Der richtige Wert hängt von der Dokumentgröße, Ingest-Pipelines, Replicas, dem Aktualisierungsintervall und der Speichergeschwindigkeit ab.

Ignorieren Sie das Betriebssystem nicht. Deaktivieren Sie Swap oder konfigurieren Sie den Host so, dass der Elasticsearch-Speicher nicht ausgelagert wird. Swapping kann ein behebbares Speicherproblem in einen langen Ausfall verwandeln, weil JVM-Pausen enorm werden. Stellen Sie sicher, dass der Prozess die Berechtigung hat, Speicher zu sperren, wenn Sie bootstrap.memory_lock: true verwenden, und überprüfen Sie dies beim Start, anstatt anzunehmen, dass die Einstellung funktioniert hat.

Starten Sie nach einer Heap-Änderung in einem Produktionscluster jeweils einen Knoten neu und warten Sie, bis sich die Shard-Wiederherstellung beruhigt hat, bevor Sie zum nächsten Knoten übergehen. Beobachten Sie die Suchlatenz, GC-Pausen, Festplattenlesevorgänge und den Indexierungsdurchsatz für mindestens einen normalen Verkehrszyklus. Eine Änderung, die in einer ruhigen Stunde gut aussieht, kann während des morgendlichen Dashboard-Ansturms fehlschlagen.

Wenn ein Knoten nach sinnvollem Tuning weiterhin unter Heap-Druck leidet, treten Sie einen Schritt zurück und fragen Sie, was der Heap enthält. Zu viele Shards, zu viele Felder, Fielddata auf Text, überdimensionierte Aggregationen, schwere Skripte und gemischte Rollen auf demselben Knoten sind häufigere Ursachen als "Elasticsearch braucht den gesamten RAM". Die stärkste Heap-Tuning-Arbeit endet oft mit einem kleineren Mapping, weniger Shards, besseren Abfragegrenzen oder einer dedizierten Ingest-Ebene.

Heap-Tuning ist keine einmalige Zeremonie. Es ist Teil des Kapazitätsmanagements. Wenn das Datenvolumen wächst, sich Dashboards ändern oder ein neues Team beginnt, breitere Dokumente zu senden, ändert sich auch das Speicherprofil. Halten Sie die Heap-Dimensionierung langweilig: Messen Sie, ändern Sie eine Sache, starten Sie sicher neu und überprüfen Sie mit echten Arbeitslastdaten.