Verständnis von "Query Then Fetch" vs. "DFS Query Then Fetch"
In unserem letzten Artikel über Starts-with-Phrase-Matching stießen wir auf eine Situation, in der die zurückgegebenen Scores verdächtig waren. Zur Erinnerung: Hier ist die betreffende Abfrage:
$ curl -XGET localhost:9200/startswith/test/_search?pretty -d '{
"query": {
"match_phrase_prefix": {
"title": {
"query": "d",
„max_expansions“: 5
}
}
}
}' | grep title
"_score" : 1.0, "_source" : {"title":"drunk"}
"_score" : 0,30685282, "_source" : {"title":"dzone"}
"_score" : 0,30685282, "_source" : {"title":"data"}
"_score" : 0,30685282, "_source" : {"title":"drive"}
Sehen Sie, wie das Dokument „drunk“ einen Score von 1,0 erhält, während die anderen einen Score von 0,3 haben? Sollten diese Dokumente nicht alle denselben Score haben, da sie gleichermaßen auf die Abfrage nach „d“ passen? Die Antwort lautet ja, aber es gibt einen sehr guten Grund für diese Diskrepanz bei der Bewertung.
Relevanzbewertung
Ein Teil des von Elasticsearch (und dem darunter liegenden Lucene) verwendeten Bewertungsalgorithmus umfasst Statistiken zur „Term Frequency – Inverse Document Frequency“ (TF-IDF), um die Relevanz von Dokumenten im Index zu berechnen.
Es wurde viel über das Thema TF-IDF geschrieben, aber im Grunde besagt es: „Je häufiger ein Begriff in einem Dokument vorkommt, desto relevanter ist dieses Dokument. Die Relevanz wird jedoch dadurch gedämpft, wie oft der Begriff im gesamten Index vorkommt“.
Seltene Begriffe sind nur in wenigen Dokumenten vorhanden, was bedeutet, dass jede Abfrage, die mit einem seltenen Begriff übereinstimmt, hochrelevant wird. Umgekehrt sind häufige Begriffe überall zu finden, sodass ihre Relevanz für die Abfrage gering ist.
Elasticsearch steht vor einem interessanten Dilemma, wenn Sie eine Suche ausführen. Ihre Abfrage muss alle relevanten Dokumente finden … aber diese Dokumente sind über eine beliebige Anzahl von Shards in Ihrem Cluster verteilt.
Jeder Shard ist im Grunde ein Lucene-Index, der seine eigenen TF- und DF-Statistiken pflegt. Ein Shard weiß nur, wie oft „pineapple“ innerhalb des Shards vorkommt, nicht im gesamten Cluster.
Aber der Relevanzalgorithmus verwendet TF-IDF … muss er nicht wissen, wie die TF und DF für den gesamten Index sind, nicht für jeden Shard?
Standard-Suchtyp: Query Then Fetch
Die Antwort lautet ja und nein. Standardmäßig verwendet Elasticsearch einen Suchtyp namens „Query Then Fetch“. Die Funktionsweise ist wie folgt:
- Senden der Abfrage an jeden Shard
- Alle übereinstimmenden Dokumente finden und Bewertungen mithilfe von lokalen Begriffs-/Dokumenthäufigkeiten berechnen
- Erstellen einer Prioritätswarteschlange für Ergebnisse (Sortierung, Paginierung mit von/bis usw.)
- Metadaten zu den Ergebnissen an den anfragenden Node zurückgeben. Hinweis: Das eigentliche Dokument wird noch nicht gesendet, nur die Scores
- Die Scores aller Shards werden auf dem anfragenden Node zusammengeführt und sortiert, Dokumente werden gemäß den Abfragekriterien ausgewählt
- Schließlich werden die eigentlichen Dokumente von den einzelnen Shards abgerufen, auf denen sie gespeichert sind.
- Die Ergebnisse werden an den Client zurückgegeben
Dieses System funktioniert normalerweise einwandfrei. In den meisten Fällen enthält Ihr Index „genug“ Dokumente, um die Statistiken zur Term-/Dokumenthäufigkeit auszugleichen. Auch wenn jede Shard möglicherweise nicht über vollständige Kenntnisse der Häufigkeiten im gesamten Cluster verfügt, sind die Ergebnisse „gut genug“, da die Häufigkeiten überall ziemlich ähnlich sind.
Aber im Ticket unserer zu Beginn dieses Artikels erwähnten Abfrage schlägt der Standard-Suchen-Typ manchmal fehl.
DFS Query Then Fetch
Im letzten Artikel haben wir einen Index erstellt, ohne die Shard-Anzahl anzugeben – Elasticsearch verwendete den Standardwert von 5 Shards. Wir haben dann mickrige fünf Dokumente in den Index eingefügt und von ES verlangt, relevante Ergebnisse und genaue Punktzahlen zurückzugeben. Nicht ganz fair, oder?
Die Diskrepanzen bei der Bewertung wurden durch den Suchen-Typ „Query Then Fetch“ verursacht. Jede Shard enthielt nur 1 oder 2 Dokumente (der von ES verwendete Hashing-Algorithmus sorgt für eine relativ zufällige Verteilung). Als wir Elastic baten, die Punktzahlen zu berechnen, hatte jede Shard nur einen winzigen Einblick in den Index mit fünf Dokumenten … daher waren die Punktzahlen ungenau.
Glücklicherweise lässt Elasticsearch Sie nicht im Stich. Wenn Sie eine Situation haben, in der diese Bewertungsdiskrepanz problematisch ist, bietet ES einen Suchen-Typ namens „DFS Query Then Fetch“ an. Das Verfahren ist fast identisch mit „Query Then Fetch“, außer dass es eine Vorabfrage durchführt, um die globalen Dokumenthäufigkeiten zu berechnen.
- Vorabfrage jedes Shards bezüglich Begriff- und Dokumenthäufigkeiten
- Senden der Abfrage an jeden Shard
- Alle übereinstimmenden Dokumente finden und Bewertungen mithilfe globaler Begriffs-/Dokumenthäufigkeiten berechnen, die aus der Vorabfrage ermittelt wurden.
- Erstellen einer Prioritätswarteschlange für Ergebnisse (Sortierung, Paginierung mit von/bis usw.)
- Metadaten zu den Ergebnissen an den anfragenden Node zurückgeben. Hinweis: Das eigentliche Dokument wird noch nicht gesendet, nur die Scores
- Die Scores aller Shards werden auf dem anfragenden Node zusammengeführt und sortiert, Dokumente werden gemäß den Abfragekriterien ausgewählt
- Schließlich werden die eigentlichen Dokumente von den einzelnen Shards abgerufen, auf denen sie gespeichert sind.
- Die Ergebnisse werden an den Client zurückgegeben
Wenn wir diesen neuen Suchtyp auf unsere vorherige Abfrage anwenden, erhalten wir Scoring-Ergebnisse, die sinnvoll sind (z. B. sind sie alle identisch):
$ curl -XGET 'localhost:9200/startswith/test/_search?pretty=true&search_type=dfs_query_then_fetch' -d '{
"query": {
"match_phrase_prefix": {
"title": {
"query": "d",
„max_expansions“: 5
}
}
}
}' | grep title
"_score" : 1,9162908, "_source" : {"title":"dzone"}
"_score" : 1,9162908, "_source" : {"title":"data"}
"_score" : 1,9162908, "_source" : {"title":"drunk"}
"_score" : 1,9162908, "_source" : {"title":"drive"}
Fazit
Natürlich gibt es eine höhere Genauigkeit nicht umsonst. Die Vorabfrage (Prequery) verursacht einen zusätzlichen Roundtrip zwischen den Shards, was je nach Größe des Index, Anzahl der Shards, Abfragerate usw. zu Performance-Einbußen führen kann. In den meisten Fällen ist dies jedoch völlig unnötig … „ausreichend“ Daten lösen das Problem für Sie.
Manchmal führen Sie jedoch in seltsame Scoring-Situationen hinein. In diesen Tickets ist es nützlich zu wissen, wie Sie den Suchausführungsplan mit „DFS Query then Fetch“ anpassen können.