Optimierung langsamer Elasticsearch-Abfragen: Best Practices für die Leistungsoptimierung
Diagnostizieren und verbessern Sie langsame Elasticsearch-Abfragen durch bessere Abfragestruktur, Paginierung, Caching, Mappings und die Profile-API.
Optimierung langsamer Elasticsearch-Abfragen: Best Practices für die Leistungsoptimierung
Langsame Elasticsearch-Abfragen haben in der Regel eine von vier Ursachen: Die Abfrage fordert zu viele Daten an, das Mapping macht die Abfrage teuer, der Cluster hat zu wenig Ressourcen, oder die Anwendung wiederholt teure Suchvorgänge, die gecached oder neu gestaltet werden sollten. Die Lösung hängt davon ab, welche Ursache zutrifft.
Bevor Sie alles umschreiben, erfassen Sie eine echte langsame Anfrage mit ihrem Index, Filtern, Sortierung, Aggregationen, Seitentiefe, Antwortgröße und Zeitmessung. Eine Dashboard-Aggregation, eine Autocomplete-Abfrage und ein Export-Job belasten Elasticsearch unterschiedlich.
Verständnis von Leistungsengpässen bei Abfragen
Bevor Sie in Lösungen eintauchen, ist es hilfreich, die häufigsten Gründe für langsame Elasticsearch-Abfragen zu verstehen. Dazu gehören oft:
- Komplexe Abfragen: Abfragen mit mehreren
bool-Klauseln, verschachtelten Abfragen oder teuren Operationen wiewildcardoderregexpauf großen Datensätzen. - Ineffizienter Datenabruf: Unnötiges Abrufen von
_sourceoder Abrufen großer Dokumentenmengen für die Paginierung. - Ressourcenbeschränkungen: Unzureichende CPU, Arbeitsspeicher oder Festplatten-I/O auf Datenknoten.
- Suboptimale Mappings: Verwendung falscher Datentypen oder Nichtnutzung von
doc_valuesfür Aggregationen. - Shard-Ungleichgewicht oder -Überlastung: Zu viele Shards, zu wenige Shards oder ungleichmäßige Verteilung von Shards/Daten.
- Cache-Fehlschläge oder schlechte Cache-Passung: Wiederholung teurer Suchvorgänge ohne Verwendung von Request-Caching, Filterkontext oder anwendungsspezifischem Caching, wo angemessen.
Optimierung der Abfragestruktur
Die Art und Weise, wie Sie Ihre Abfragen konstruieren, hat einen tiefgreifenden Einfluss auf ihre Leistung. Kleine Änderungen können zu erheblichen Verbesserungen führen.
1. Nur notwendige Felder abrufen (_source-Filterung & stored_fields)
Standardmäßig gibt Elasticsearch das gesamte _source-Feld für jedes übereinstimmende Dokument zurück. Wenn Ihre Dokumente groß sind und die Benutzeroberfläche nur einen Titel, eine ID und einen Zeitstempel benötigt, verschwendet das Abrufen des gesamten Dokuments Netzwerkbandbreite und Parsing-Zeit.
_source-Filterung: Verwenden Sie den_source-Parameter, um ein Array von Feldern anzugeben, die ein- oder ausgeschlossen werden sollen.GET /my-index/_search { "_source": ["title", "author", "publish_date"], "query": { "match": { "content": "Elasticsearch performance" } } }stored_fields: Wenn Sie bestimmte Felder explizit in Ihrem Mapping gespeichert haben ("store": true), können Sie diese mitstored_fieldsabrufen. Die meisten Bereitstellungen speichern auf diese Weise nicht viele Felder, daher ist die_source-Filterung die häufigere Lösung.GET /my-index/_search { "stored_fields": ["title", "author"], "query": { "match": { "content": "Elasticsearch performance" } } }
2. Bevorzugen Sie effiziente Abfragetypen
Einige Abfragetypen sind von Natur aus ressourcenintensiver als andere.
Vermeiden Sie führende Wildcards und breite Regexps:
wildcard- undregexp-Abfragen können teuer sein, insbesondere mit führenden Wildcards wie*test. Präfix-Abfragen sind in der Regel besser handhabbar als Suchvorgänge mit führenden Wildcards, benötigen aber dennoch sinnvolle Mappings und begrenzte Eingaben.# Ineffizient - führende Wildcard vermeiden { "query": { "wildcard": { "name.keyword": { "value": "*search" } } } } # Besser - wenn Sie das Präfix kennen { "query": { "prefix": { "name.keyword": { "value": "Elastic" } } } }Verwenden Sie
match_phrasefür Phrasenabsicht: Wenn der Benutzer nach einer exakten Phrase sucht, drücktmatch_phrasediese Absicht besser aus als mehrere nicht zusammenhängendematch-Klauseln. Es ist nicht immer günstiger, vermeidet aber die Rückgabe von Dokumenten, die die Wörter nur weit voneinander entfernt enthalten.Filterkontext für Ja/Nein-Bedingungen: Wenn Sie nur wissen möchten, ob ein Dokument einer Bedingung entspricht, setzen Sie diese Bedingung in den
filter-Kontext oder verwenden Sieconstant_score. Dies vermeidet unnötige Bewertungsarbeit und ist cache-freundlicher.GET /my-index/_search { "query": { "constant_score": { "filter": { "term": { "status": "active" } } } } }
3. Optimieren Sie Boolesche Abfragen
- Verwenden Sie Filter für strukturierte Einschränkungen: Setzen Sie Mandanten-IDs, Statuswerte, Datumsbereiche und exakte Tags in
filter, nicht inmust, es sei denn, sie benötigen eine Bewertung. Elasticsearch kann Klauseln intern neu anordnen und optimieren, verlassen Sie sich daher nicht auf die JSON-Reihenfolge als Ihr Hauptleistungswerkzeug. - Verwenden Sie
minimum_should_matchgezielt: Es kann die Relevanz verbessern und breite Übereinstimmungen reduzieren, aber eine zu hohe Einstellung kann gültige Ergebnisse verbergen.
4. Effiziente Paginierung (search_after und scroll)
Die traditionelle from/size-Paginierung wird für tiefe Seiten (z. B. from: 10000, size: 10) sehr ineffizient. Elasticsearch muss alle Dokumente bis zu from + size auf jedem Shard abrufen und sortieren, dann from-Dokumente verwerfen.
search_after: Für Echtzeit-Tiefenpaginierung wirdsearch_afterempfohlen. Es verwendet die Sortierreihenfolge des letzten Dokuments der vorherigen Seite, um den nächsten Satz von Ergebnissen zu finden, ähnlich wie Cursor in traditionellen Datenbanken. Es ist zustandslos und skaliert besser.# Erste Anfrage GET /my-index/_search { "size": 10, "query": {"match_all": {}}, "sort": [{"timestamp": "asc"}, {"_id": "asc"}] } # Nachfolgende Anfrage unter Verwendung der Sortierwerte des letzten Dokuments der ersten Anfrage GET /my-index/_search { "size": 10, "query": {"match_all": {}}, "search_after": [1678886400000, "doc_id_XYZ"], "sort": [{"timestamp": "asc"}, {"_id": "asc"}] }scroll-API: Für den Massenabruf großer Datensätze, wie z. B. beim Reindexing oder Export, kannscrollweiterhin nützlich sein. Für neuere Elasticsearch-Versionen und langlaufende Vollindex-Scans sollten Sie auch Point-in-Time plussearch_afterin Betracht ziehen. Scroll ist nicht für benutzerorientierte Echtzeit-Paginierung geeignet.
5. Optimierung von Aggregationen
Aggregationen können ressourcenintensiv sein, insbesondere bei Feldern mit hoher Kardinalität.
- Vorberechnung von Aggregationen: Erwägen Sie, komplexe, nicht in Echtzeit durchgeführte Aggregationen während der Indexierung oder nach einem Zeitplan auszuführen, um Ergebnisse vorzuberechnen und in einem separaten Index zu speichern.
doc_values: Stellen Sie sicher, dass für Felder, die in Aggregationen verwendet werden,doc_valuesaktiviert sind (was für die meisten Nicht-Text-Felder der Standard ist). Dadurch kann Elasticsearch Daten für Aggregationen effizient laden, ohne_sourceladen zu müssen.eager_global_ordinals: Fürkeyword-Felder, die häufig interms-Aggregationen verwendet werden, kann das Setzen voneager_global_ordinals: trueim Mapping die Leistung verbessern, indem globale Ordinals vorab erstellt werden. Dies verursacht Kosten zum Zeitpunkt der Indexaktualisierung, beschleunigt aber Aggregationen zur Abfragezeit.
Nutzung von Caching-Techniken
Elasticsearch bietet mehrere Caching-Ebenen, die wiederholte Abfragen erheblich beschleunigen können.
1. Node Query Cache
- Mechanismus: Cached die Ergebnisse von Filterklauseln innerhalb von
bool-Abfragen, die häufig verwendet werden. Es ist ein In-Memory-Cache auf Knotenebene. - Effektivität: Am effektivsten für wiederholte Filterklauseln. Verlassen Sie sich nicht darauf für jede Abfrage; Elasticsearch entscheidet, was sich zu cachen lohnt.
- Konfiguration: Standardmäßig aktiviert. Sie können seine Größe mit
indices.queries.cache.sizesteuern (Standard 10% des Heaps).
2. Shard Request Cache
Mechanismus: Cached Suchergebnisse auf Shard-Ebene, am häufigsten für aggregationslastige Anfragen mit
size=0. Es ist eine gute Wahl für wiederholte Dashboard-Abfragen über Daten, die sich nicht jede Sekunde ändern.Effektivität: Hervorragend für Dashboard-Abfragen oder analytische Anwendungen, bei denen dieselbe Anfrage (einschließlich Aggregationen) wiederholt mit identischen Parametern ausgeführt wird.
Verwendung: Aktivieren Sie es explizit in Ihrer Abfrage mit
"request_cache": true.GET /my-index/_search?request_cache=true { "size": 0, "query": { "bool": { "filter": [ {"term": {"status.keyword": "active"}}, {"range": {"timestamp": {"gte": "now-1h"}}} ] } }, "aggs": { "messages_per_minute": { "date_histogram": { "field": "timestamp", "fixed_interval": "1m" } } } }Einschränkungen: Der Cache wird ungültig, sobald ein Shard aktualisiert wird (neue Dokumente werden indiziert oder vorhandene aktualisiert). Nur nützlich für Abfragen, die häufig identische Ergebnisse zurückgeben.
3. Dateisystem-Cache (Betriebssystemebene)
- Mechanismus: Der Dateisystem-Cache des Betriebssystems spielt eine entscheidende Rolle. Elasticsearch verlässt sich stark darauf, um häufig aufgerufene Indexsegmente zu cachen.
- Effektivität: Entscheidend für die Abfrageleistung. Wenn sich Indexsegmente im RAM befinden, wird der Festplatten-I/O vollständig umgangen, was zu einer viel schnelleren Abfrageausführung führt.
- Best Practice: Lassen Sie ausreichend RAM für den Dateisystem-Cache. Ein üblicher Ausgangspunkt ist, den JVM-Heap bei etwa der Hälfte des Systemspeichers zu halten, unter Berücksichtigung der üblichen Elasticsearch-Heap-Grenzen, und dann mit Ihrer Arbeitslast zu validieren.
4. Anwendungsspezifisches Caching
- Mechanismus: Implementierung eines Caches auf Ihrer Anwendungsebene (z. B. mit Redis, Memcached oder einem In-Memory-Cache) für häufig angeforderte Suchergebnisse.
- Effektivität: Kann die schnellsten Antwortzeiten bieten, indem Elasticsearch für wiederholte Anfragen vollständig umgangen wird. Am besten für statische oder sich langsam ändernde Suchergebnisse geeignet.
- Überlegungen: Die Cache-Invalidierungsstrategie ist entscheidend. Erfordert sorgfältiges Design, um Datenkonsistenz zu gewährleisten.
Verwendung der Profile-API zur Identifizierung von Engpässen
Die Profile-API ist ein unschätzbares Werkzeug, um genau zu verstehen, wie Elasticsearch eine Abfrage ausführt und wo Zeit verbraucht wird. Sie unterteilt die Ausführungszeit für jede Komponente Ihrer Abfrage und Aggregation.
So verwenden Sie die Profile-API
Fügen Sie einfach "profile": true zu Ihrem Suchanfragekörper hinzu.
GET /my-index/_search
{
"profile": true,
"query": {
"bool": {
"must": [
{"match": {"title": "Elasticsearch"}},
{"term": {"status.keyword": "published"}}
],
"filter": [
{"range": {"publish_date": {"gte": "2023-01-01"}}}
]
}
},
"aggs": {
"top_authors": {
"terms": {
"field": "author.keyword",
"size": 10
}
}
}
}
Interpretation der Profile-API-Ergebnisse
Die Antwort enthält einen profile-Abschnitt, der die Ausführung von Abfragen und Aggregationen auf jedem Shard detailliert beschreibt. Wichtige Metriken, auf die Sie achten sollten, sind:
description: Die spezifische Abfrage- oder Aggregationskomponente.time_in_nanos: Die Zeit, die für die Ausführung dieser Komponente aufgewendet wurde.breakdown: Detaillierte Untermetriken wiebuild_scorer_time,collect_time,set_weight_timefür Abfragen undreduce_timefür Aggregationen.children: Verschachtelte Komponenten, die zeigen, wie die Zeit innerhalb komplexer Abfragen verteilt ist.
Beispielinterpretation:
Wenn Sie eine hohe time_in_nanos für eine WildcardQuery sehen, bestätigt dies, dass dies ein teurer Teil Ihrer Abfrage ist. Wenn collect_time hoch ist, deutet dies darauf hin, dass das Abrufen und Verarbeiten von Dokumenten nach einer Übereinstimmung ein Engpass ist, möglicherweise aufgrund von _source-Parsing oder tiefer Paginierung. Eine hohe reduce_time bei Aggregationen könnte auf eine hohe Last während der finalen Zusammenführungsphase hinweisen.
Durch die Untersuchung dieser Metriken können Sie bestimmte Abfrageklauseln oder Aggregationsfelder identifizieren, die die meisten Ressourcen verbrauchen, und dann die zuvor besprochenen Optimierungstechniken anwenden.
Allgemeine Best Practices für die Leistung
Über abfragespezifische Optimierungen hinaus tragen mehrere clusterweite und indexebene Best Practices zur allgemeinen Suchleistung bei.
1. Optimale Index-Mappings
textvs.keyword: Verwenden Sietextfür die Volltextsuche undkeywordfür exakte Wertübereinstimmungen, Sortierung und Aggregationen. Nicht übereinstimmende Typen können zu ineffizienten Abfragen führen.doc_values: Stellen Sie sicher, dassdoc_valuesfür Felder aktiviert sind, die Sie sortieren oder aggregieren möchten. Sie sind standardmäßig für die meisten Feldtypen aktiviert, die Sortierung und Aggregationen unterstützen, wiekeyword, numerische, Datums-, Boolesche und IP-Felder. Reinetext-Felder sind für die Volltextsuche; verwenden Sie einkeyword-Unterfeld, wenn Sie exakte Übereinstimmungen oder Aggregationen benötigen.norms: Deaktivieren Sienorms("norms": false) für Felder, bei denen Sie keine Dokumentlängennormalisierung benötigen (z. B. ID-Felder). Dies spart Speicherplatz und verbessert die Indexierungsgeschwindigkeit bei minimalen Auswirkungen auf die Abfrageleistung für nicht bewertende Abfragen.index_options: Verwenden Sie fürtext-Felderindex_options: docs, wenn Sie nur wissen müssen, ob ein Begriff in einem Dokument vorhanden ist, undindex_options: positions(der Standard), wenn Sie Phrasenabfragen und Näherungssuchen benötigen.
2. Überwachen Sie die Cluster-Gesundheit und -Ressourcen
- Cluster-Status: Grün ist das Ziel. Gelb bedeutet, dass ein oder mehrere Replikat-Shards nicht zugewiesen sind; Suchvorgänge können weiterhin funktionieren, aber die Ausfallsicherheit ist verringert und die Leistung kann leiden. Rot bedeutet, dass primäre Shards fehlen und einige Daten nicht verfügbar sind.
- Ressourcenüberwachung: Überwachen Sie regelmäßig CPU, RAM, Festplatten-I/O und Netzwerkauslastung auf Ihren Datenknoten. Spitzen in diesen Metriken korrelieren oft mit langsamen Abfragen.
- JVM-Heap: Behalten Sie die JVM-Heap-Auslastung im Auge. Eine hohe Auslastung kann zu häufigen Garbage-Collection-Pausen führen, die Abfragen verlangsamen. Optimieren Sie Abfragen, um den Heap-Druck zu reduzieren.
3. Richtige Shard-Zuweisung
- Zu viele Shards: Jeder Shard verbraucht Ressourcen. Viele kleine Shards erzeugen Overhead. Shards im zweistelligen Gigabyte-Bereich sind üblich, aber die richtige Größe hängt von Heap, Abfragemuster, Wiederherstellungszielen und Hardware ab.
- Zu wenige Shards: Begrenzt die Parallelität. Abfragen gegen einen Index mit zu wenigen Shards können nicht alle verfügbaren Datenknoten effizient nutzen.
4. Indexierungsstrategie
- Aktualisierungsintervall: Ein niedrigeres
refresh_interval(Standard 1 Sekunde) macht Daten schneller sichtbar, erhöht aber den Indexierungsaufwand. Für suchlastige Workloads sollten Sie es leicht erhöhen (z. B. 5-10 Sekunden), um den Aktualisierungsdruck zu verringern.
Der praktische Arbeitsablauf ist einfach: Finden Sie die echte langsame Abfrage, profilieren Sie sie, reduzieren Sie die Datenmenge, die sie berührt, und passen Sie das Mapping an die Art und Weise an, wie Benutzer suchen. Wenn die Abfrage bereits sauber ist, überprüfen Sie das Shard-Layout, den Heap-Druck, den Dateisystem-Cache und den Festplatten-I/O. Elasticsearch ist schnell, wenn das Indexdesign, die Abfrageform und die Cluster-Ressourcen übereinstimmen.