Fehlerbehebung bei häufigen Elasticsearch-Leistungsengpässen

Ein praktischer Workflow zur Identifizierung von Elasticsearch-Leistungsengpässen bei Indizierung, Suche, Heap, Speicher und Shard-Design.

Fehlerbehebung bei häufigen Elasticsearch-Leistungsengpässen

Die Fehlerbehebung bei Elasticsearch-Leistungsengpässen funktioniert am besten, wenn Sie der ersten einfachen Theorie widerstehen. Ein langsames Dashboard kann eine schlechte Abfrage sein, aber es kann auch ein heißer Shard, eine gesättigte Festplatte, ein Heap-Problem, ein Mapping-Fehler oder ein Wiederherstellungsprozess sein, der um I/O konkurriert. Beginnen Sie mit Beweisen und grenzen Sie dann den Umfang ein.

Ich unterteile die Frage normalerweise in drei Teile: Was ist langsam, wo ist es langsam und was hat sich geändert. „Elasticsearch ist langsam“ ist nicht umsetzbar. „Die Suchlatenz für logs-prod-* hat sich nach der gestrigen Mapping-Änderung verdoppelt, hauptsächlich auf zwei Datenknoten“ gibt Ihnen einen Ansatzpunkt.

Diagnose von Leistungsproblemen

Bevor Sie sich mit spezifischen Lösungen befassen, ist es wichtig, Werkzeuge und Methoden zur Diagnose von Leistungsproblemen zu haben. Elasticsearch bietet mehrere APIs und Metriken, die für diesen Prozess unverzichtbar sind.

Wichtige Werkzeuge und Metriken:

  • Cluster Health API (_cluster/health): Bietet einen Überblick über den Status des Clusters (grün, gelb, rot), die Anzahl der Knoten, Shards und ausstehenden Aufgaben. Eine hohe Anzahl ausstehender Aufgaben kann auf Indizierungs- oder Wiederherstellungsprobleme hinweisen.
  • Node Stats API (_nodes/stats): Bietet detaillierte Statistiken für jeden Knoten, einschließlich CPU-Auslastung, Speicher, Festplatten-I/O, Netzwerkverkehr und JVM-Heap-Nutzung. Dies ist entscheidend für die Identifizierung ressourcengebundener Knoten.
  • Index Stats API (_stats): Bietet Statistiken für einzelne Indizes, wie Indizierungsraten, Suchraten und Cache-Nutzung. Dies hilft, problematische Indizes zu identifizieren.
  • Slow Log: Elasticsearch kann langsame Indizierungs- und Suchanfragen protokollieren. Slow-Log-Schwellenwerte sind Indexeinstellungen, sodass Sie sie auf einen lauten Index anwenden können, anstatt den gesamten Cluster in einen Log-Generator zu verwandeln.
    • Indizierungs-Slow-Log: Nützlich, wenn Bulk-Schreibvorgänge pausieren oder die Ingest-Latenz steigt.
    • Such-Slow-Log: Nützlich, wenn Sie das tatsächliche Anforderungsmuster benötigen, nicht nur ein Latenzdiagramm.
  • Überwachungswerkzeuge: Lösungen wie Kibanas Monitoring-UI, Prometheus mit dem Elasticsearch Exporter oder kommerzielle APM-Tools bieten Dashboards und historische Daten für eine tiefergehende Analyse.

Häufige Engpässe und Lösungen

1. Langsame Indizierung

Langsame Indizierung kann durch verschiedene Faktoren verursacht werden, darunter Netzwerklatenz, Festplatten-I/O-Engpässe, unzureichende Ressourcen, ineffizientes Mapping oder suboptimale Nutzung der Bulk-API.

Ursachen und Lösungen:
  • Festplatten-I/O-Sättigung: Elasticsearch ist stark auf schnelle Festplatten-I/O für die Indizierung angewiesen. SSDs werden dringend empfohlen.

    • Diagnose: Überwachen Sie die Festplatten-Lese-/Schreib-IOPS und den Durchsatz mit _nodes/stats oder OS-Level-Tools. Achten Sie auf hohe Warteschlangentiefen.
    • Lösung: Upgrade auf schnellere Speicher (SSDs), verteilen Sie Shards auf mehr Knoten oder optimieren Sie Ihre Shard-Strategie, um die I/O pro Knoten zu reduzieren.
  • JVM-Heap-Druck: Wenn der JVM-Heap ständig unter Druck steht, kann die Garbage Collection zu einem erheblichen Engpass werden und alle Operationen, einschließlich der Indizierung, verlangsamen.

    • Diagnose: Überwachen Sie die JVM-Heap-Nutzung in Kibana Monitoring oder _nodes/stats. Hohe Heap-Nutzung und häufige, lange Garbage-Collection-Pausen sind Warnsignale.
    • Lösung: Erhöhen Sie die JVM-Heap-Größe (aber nicht über 50 % des System-RAM und nicht über 30,5 GB), optimieren Sie Mappings, um die Dokumentgröße zu reduzieren, oder fügen Sie weitere Knoten hinzu, um die Last zu verteilen.
  • Ineffizientes Mapping: Übermäßig komplexe Mappings, dynamisches Mapping mit vielen neu erstellten Feldern oder falsche Datentypen können den Indizierungsaufwand erhöhen.

    • Diagnose: Analysieren Sie Index-Mappings (_mapping API). Achten Sie auf verschachtelte Objekte, eine große Anzahl von Feldern oder unnötig indizierte Felder.
    • Lösung: Definieren Sie explizite Mappings mit geeigneten Datentypen. Verwenden Sie dynamic: false oder dynamic: strict, wo anwendbar. Vermeiden Sie tief verschachtelte Strukturen, wenn sie nicht unbedingt erforderlich sind.
  • Netzwerklatenz: Hohe Latenz zwischen Knoten oder zwischen Clients und dem Cluster kann Bulk-Indizierungsanfragen verlangsamen.

    • Diagnose: Messen Sie die Netzwerklatenz zwischen Ihren Clients/Knoten. Analysieren Sie die Antwortzeiten der Bulk-API.
    • Lösung: Halten Sie Cluster-Knoten in einem latenzarmen privaten Netzwerk, platzieren Sie Bulk-Clients nach Möglichkeit in der Nähe des Clusters und reduzieren Sie unnötigen regionsübergreifenden Traffic. Die Einstellungen des Request-Caches beheben keine Netzwerklatenz.
  • Suboptimale Nutzung der Bulk-API: Das Senden einzelner Anfragen anstelle von Bulk-Anfragen oder das Senden übermäßig großer/kleiner Bulk-Anfragen kann ineffizient sein.

    • Diagnose: Überwachen Sie den Durchsatz Ihrer Bulk-Indizierung. Analysieren Sie die Größe Ihrer Bulk-Anfragen.
    • Lösung: Verwenden Sie die Bulk-API für alle Indizierungsvorgänge. Experimentieren Sie mit der Bulk-Größe (typischerweise 5-15 MB pro Bulk-Anfrage ist ein guter Ausgangspunkt), um das optimale Gleichgewicht zwischen Durchsatz und Latenz zu finden. Stellen Sie sicher, dass Ihre Bulk-Anfragen ordnungsgemäß gebündelt sind.
  • Translog-Haltbarkeit: Die Einstellung index.translog.durability steuert, wie oft das Transaktionslog auf die Festplatte geschrieben wird. request (Standard) ist sicherer, kann aber die Leistung im Vergleich zu async beeinträchtigen.

    • Diagnose: Dies ist eine Konfigurationseinstellung.
    • Lösung: Für maximalen Indizierungsdurchsatz erwägen Sie async-Haltbarkeit. Beachten Sie jedoch, dass dies das Risiko von Datenverlust im Falle eines Knotenabsturzes zwischen den Schreibvorgängen erhöht.

2. Langsame Abfragen

Die Abfrageleistung wird durch Shard-Größe, Abfragekomplexität, Caching und die Effizienz der zugrunde liegenden Datenstruktur beeinflusst.

Ursachen und Lösungen:
  • Große Shards: Zu große Shards können Abfragen verlangsamen, da Elasticsearch mehr Daten durchsuchen und Ergebnisse aus mehreren Segmenten zusammenführen muss.

    • Diagnose: Überprüfen Sie die Shard-Größen mit _cat/shards oder _all/settings?pretty.
    • Lösung: Streben Sie Shard-Größen zwischen 10 GB und 50 GB an. Erwägen Sie, Daten in einen neuen Index mit kleineren Shards neu zu indizieren oder Index Lifecycle Management (ILM) zu verwenden, um die Shard-Größe im Laufe der Zeit zu verwalten.
  • Zu viele Shards: Eine übermäßige Anzahl kleiner Shards kann zu einem hohen Overhead für den Cluster führen, insbesondere bei Suchen. Jeder Shard benötigt Ressourcen für die Verwaltung.

    • Diagnose: Zählen Sie die Gesamtzahl der Shards pro Knoten und pro Index mit _cat/shards.
    • Lösung: Konsolidieren Sie Indizes, wenn möglich. Optimieren Sie Ihr Datenmodell, um die Anzahl der Indizes und damit die Gesamtzahl der Shards zu reduzieren. Für Zeitreihendaten kann ILM bei der Verwaltung der Shard-Anzahl helfen.
  • Ineffiziente Abfragen: Komplexe Abfragen, Abfragen mit umfangreichem Scripting, Wildcard-Suchen am Anfang von Begriffen oder reguläre Ausdrücke können sehr ressourcenintensiv sein.

    • Diagnose: Verwenden Sie die Profile API (_search?profile=true), um die Abfrageausführungszeit zu analysieren und langsame Teile zu identifizieren. Analysieren Sie Slow Logs.
    • Lösung: Vereinfachen Sie Abfragen. Vermeiden Sie führende Wildcards und teure Regex. Verwenden Sie term-Abfragen anstelle von match für exakte Übereinstimmungen, wo möglich. Erwägen Sie die Verwendung von search_as_you_type oder completion-Suggestern für Vorschläge während der Eingabe. Optimieren Sie Filterklauseln (verwenden Sie den filter-Kontext anstelle des query-Kontexts für nicht bewertende Abfragen).
  • Mangel an Caching: Unzureichendes oder ineffektives Caching kann zu wiederholten Berechnungen und Datenabrufen führen.

    • Diagnose: Überwachen Sie die Cache-Trefferquoten für den Query-Cache und den Request-Cache mit _nodes/stats/indices/query_cache und _nodes/stats/indices/request_cache.
    • Lösung: Stellen Sie sicher, dass angemessenes Caching aktiviert ist. Der Filter-Cache (Teil des Query-Cache) ist besonders wichtig für wiederholte Filterabfragen. Für häufig ausgeführte identische Abfragen erwägen Sie, den Request-Cache zu aktivieren.
  • Segment-Merging-Overhead: Elasticsearch führt im Hintergrund kleinere Segmente zu größeren zusammen. Dieser Prozess verbraucht I/O- und CPU-Ressourcen, was manchmal die Echtzeit-Abfrageleistung beeinträchtigen kann.

    • Diagnose: Überwachen Sie die Anzahl der Segmente pro Shard mit _cat/segments.
    • Lösung: Vermeiden Sie es, Merge-Einstellungen leichtfertig zu ändern. Reduzieren Sie während eines großen Backfills die Aktualisierungshäufigkeit, kontrollieren Sie die Bulk-Konkurrenz und beobachten Sie die Merge-Drosselung und die Festplatten-I/O. Force-Merges sind normalerweise für schreibgeschützte Indizes gedacht, nicht für aktive heiße Indizes.

3. Ressourcenkonkurrenz (CPU, Speicher, Netzwerk)

Ressourcenkonkurrenz ist eine breite Kategorie, die sich sowohl in der Indizierungs- als auch in der Abfrageleistungsverschlechterung manifestieren kann.

Ursachen und Lösungen:
  • CPU-Überlastung: Hohe CPU-Auslastung kann durch komplexe Abfragen, intensive Aggregationen, zu viele Indizierungsvorgänge oder übermäßige Garbage Collection verursacht werden.

    • Diagnose: Überwachen Sie die CPU-Auslastung pro Knoten (_nodes/stats). Identifizieren Sie, welche Operationen die meiste CPU verbrauchen (z. B. Suche, Indizierung, JVM GC).
    • Lösung: Optimieren Sie Abfragen und Aggregationen. Verteilen Sie die Last auf mehr Knoten. Reduzieren Sie die Indizierungsrate, wenn sie die CPU überlastet. Stellen Sie angemessene JVM-Heap-Einstellungen sicher, um den GC-Overhead zu minimieren.
  • Speicherprobleme (JVM-Heap und Systemspeicher): Unzureichender JVM-Heap führt zu häufiger GC. Wenn der Systemspeicher ausgeht, kann dies zu Swapping führen, was die Leistung drastisch reduziert.

    • Diagnose: Überwachen Sie die JVM-Heap-Nutzung und den gesamten Systemspeicher (RAM, Swap) auf jedem Knoten.
    • Lösung: Weisen Sie ausreichend JVM-Heap zu (z. B. 50 % des System-RAM, bis zu 30,5 GB). Vermeiden Sie Swapping, indem Sie genügend freien Systemspeicher sicherstellen. Erwägen Sie, weitere Knoten hinzuzufügen oder dedizierte Knoten für bestimmte Rollen (Master, Daten, Ingest) zu verwenden.
  • Netzwerkengpässe: Hoher Netzwerkverkehr kann die Kommunikation zwischen Knoten, die Replikation und Client-Anfragen verlangsamen.

    • Diagnose: Überwachen Sie die Netzwerkbandbreitennutzung und Latenz zwischen Knoten und Clients.
    • Lösung: Optimieren Sie die Netzwerkinfrastruktur. Reduzieren Sie unnötige Datenübertragungen. Stellen Sie optimale Shard-Zuweisungs- und Replikationseinstellungen sicher.
  • Festplatten-I/O-Sättigung: Wie bei der Indizierung erwähnt, wirkt sich dies auch auf die Abfrageleistung aus, wenn Daten von der Festplatte gelesen werden.

    • Diagnose: Überwachen Sie die Festplatten-I/O-Metriken.
    • Lösung: Upgrade auf schnellere Speicher, verteilen Sie Daten auf mehr Knoten oder optimieren Sie Abfragen, um die Menge der gelesenen Daten zu reduzieren.

Best Practices für die Leistungsoptimierung

  • Kontinuierlich überwachen: Leistungsoptimierung ist ein fortlaufender Prozess. Überwachen Sie regelmäßig die Gesundheit Ihres Clusters und die Ressourcennutzung.
  • Mappings optimieren: Definieren Sie explizite, effiziente Mappings, die auf Ihre Daten zugeschnitten sind. Vermeiden Sie unnötige Felder oder Indizierung.
  • Shard-Strategie: Streben Sie optimale Shard-Größen an (10-50 GB) und vermeiden Sie zu viele oder zu wenige Shards.
  • Bulk-API verwenden: Verwenden Sie die Bulk-API für die Indizierung und die Multi-Search-API, wenn Sie unabhängige Suchen bündeln müssen.
  • JVM-Heap optimieren: Weisen Sie ausreichend Heap zu, aber überallozieren Sie nicht. Vermeiden Sie Swapping.
  • Abfrageleistung verstehen: Profilieren Sie Abfragen, vereinfachen Sie sie und nutzen Sie den Filterkontext.
  • Caching nutzen: Stellen Sie sicher, dass Query- und Request-Caches effektiv genutzt werden.
  • Hardware: Verwenden Sie SSDs für die Speicherung und stellen Sie ausreichend CPU und RAM sicher.
  • Dedizierte Knoten: Erwägen Sie die Verwendung dedizierter Knoten für Master-, Daten- und Ingest-Rollen, um Arbeitslasten zu isolieren.
  • Index Lifecycle Management (ILM): Für Zeitreihendaten ist ILM unerlässlich, um Indizes zu verwalten, Shards zu rollieren und schließlich alte Daten zu löschen, was hilft, die Shard-Anzahl und -Größe zu kontrollieren.

Wenn Sie einen Engpass finden, nehmen Sie die kleinste Änderung vor, die ihn direkt behebt. Fügen Sie Knoten hinzu, wenn der Cluster tatsächlich keine Kapazität mehr hat. Beheben Sie Mappings, wenn Heap verschwendet wird. Schreiben Sie Abfragen um, wenn die Profilausgabe auf teure Klauseln hinweist. Passen Sie die Shard-Strategie an, wenn ein Knoten Arbeit erledigt, die verteilt werden sollte. Diese Disziplin verhindert, dass Leistungsarbeit zu einem Haufen nicht zusammenhängender Stellschrauben wird.