Ungefähre nächste Nachbarn finden, Vektorindizes erstellen und Vektoreinbettungen abfragen

Auf dieser Seite wird beschrieben, wie Sie mit den folgenden ANN-Entfernungsfunktionen in Spanner die ungefähren nächsten Nachbarn (ANN) ermitteln, Vektorindexe erstellen und Vektoreinbettungen abfragen:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

Wenn ein Dataset klein ist, können Sie k-nächstgelegene Nachbarn (KNN) verwenden, um die genauen k-nächsten Vektoren zu ermitteln. Wenn Ihr Dataset jedoch wächst, steigen auch die Latenz und die Kosten einer KNN-Suche. Sie können ANN verwenden, um die ungefähren k-nächsten Nachbarn mit erheblich geringerer Latenz und geringeren Kosten zu ermitteln.

Ungefähre k-nächste Nachbarn

Bei einer ANN-Suche sind die k-zurückgegebenen Vektoren nicht die tatsächlichen k-nächsten Nachbarn, da die ANN-Suche ungefähre Entfernungen berechnet und möglicherweise nicht alle Vektoren im Datensatz berücksichtigt. Gelegentlich werden einige Vektoren zurückgegeben, die nicht zu den obersten k-nächsten Nachbarn gehören. Das wird als Rückrufverlust bezeichnet. Wie viel der Recall-Verlust für Sie akzeptabel ist, hängt vom Anwendungsfall ab. In den meisten Fällen ist jedoch ein geringer Recall im Gegenzug für eine verbesserte Datenbankleistung ein akzeptabler Kompromiss.

Weitere Informationen zu den Spanner-Funktionen für die ungefähre Entfernung finden Sie unter:

Vektorindex

Spanner beschleunigt die ANN-Vektorsuche mithilfe eines spezialisierten Vektorindex. Dieser Index nutzt den skalierbaren nächsten Nachbarn (ScaNN) von Google Research, einen hocheffizienten Algorithmus für den nächsten Nachbarn.

Der Vektorindex verwendet eine baumbasierte Struktur, um Daten zu partitionieren und schnellere Suchvorgänge zu ermöglichen. Spanner bietet sowohl Baumkonfigurationen mit zwei als auch mit drei Ebenen:

  • Baumkonfiguration auf zwei Ebenen: Blattknoten (num_leaves) enthalten Gruppen eng verwandter Vektoren und ihren entsprechenden Schwerpunkt. Die Stammebene besteht aus den Schwerpunkten aller Blattknoten.
  • Baumkonfiguration mit drei Ebenen: Ähnlich wie ein Baum mit zwei Ebenen, mit dem Unterschied, dass eine zusätzliche Zweigebene (num_branches) eingeführt wird, von der Blattknotenschwerpunkte weiter partitioniert werden, um die Stammebene (num_leaves) zu bilden.

Spanner wählt einen Index für Sie aus. Wenn Sie jedoch wissen, dass ein bestimmter Index am besten funktioniert, können Sie den FORCE_INDEX-Hinweis verwenden, um den am besten geeigneten Vektorindex für Ihren Anwendungsfall zu verwenden.

Weitere Informationen findest du unter VECTOR INDEX-Anweisungen.

Beschränkungen

Vektorindex erstellen

Um die Trefferquote und die Leistung eines Vektorindex zu optimieren, empfehlen wir Folgendes:

  • Erstellen Sie den Vektorindex, nachdem die meisten Zeilen mit Einbettungen in die Datenbank geschrieben wurden. Möglicherweise müssen Sie den Vektorindex auch regelmäßig neu erstellen, nachdem Sie neue Daten eingefügt haben. Weitere Informationen finden Sie unter Vektorindex neu erstellen.

  • Mit der STORING-Klausel können Sie eine Kopie einer Spalte im Vektorindex speichern. Wenn ein Spaltenwert im Vektorindex gespeichert ist, filtert Spanner auf der Blattebene des Index, um die Abfrageleistung zu verbessern. Wir empfehlen, eine Spalte zu speichern, wenn sie in einer Filterbedingung verwendet wird. Weitere Informationen zur Verwendung von STORING in einem Index finden Sie unter Index für reine Indexscans erstellen.

Beim Erstellen der Tabelle muss die Einbettungsspalte ein Array des Datentyps FLOAT32 (empfohlen) oder FLOAT64 sein und die Anmerkung vector_length haben, die die Dimension der Vektoren angibt.

Mit der folgenden DDL-Anweisung wird eine Documents-Tabelle mit einer Einbettungsspalte DocEmbedding mit einer Vektorlänge erstellt:

CREATE TABLE Documents {
  ...
  DocEmbedding ARRAY<FLOAT32>(vector_length=>128);
};

Nachdem Sie die Tabelle Documents ausgefüllt haben, können Sie einen Vektorindex mit einer zweistufigen Baumstruktur und 1.000 Blattknoten in einer Documents-Tabelle mit einer Einbettungsspalte DocEmbedding unter Verwendung der Kosinus-Distanz erstellen:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding)
  STORING (WordCount)
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

So erstellen Sie einen Vektorindex mit einer Baumstruktur mit drei Ebenen und 1.000.000 Blattknoten:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  STORING (WordCount)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);

Wenn die Einbettungsspalte in der Tabellendefinition nicht als NOT NULL gekennzeichnet ist, müssen Sie sie mit einer WHERE column_name IS NOT NULL-Klausel deklarieren:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  STORING (WordCount)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

Vektoreinbettungen abfragen

Verwenden Sie zum Abfragen eines Vektorindex eine der drei ungefähren Entfernungsfunktionen:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

Zu den Einschränkungen bei der Verwendung der Funktionen für ungefähre Entfernungen gehören:

  • Mit der Funktion für ungefähre Entfernungen muss die Entfernung zwischen einer eingebetteten Spalte und einem konstanten Ausdruck (z. B. einem Parameter oder Literal) berechnet werden.
  • Die Ausgabe der Funktion für die ungefähre Entfernung muss in einer ORDER BY-Klausel als einzigen Sortierschlüssel verwendet werden und nach ORDER BY muss ein LIMIT angegeben werden.
  • Die Abfrage muss Zeilen, die nicht indexiert sind, explizit herausfiltern. In den meisten Fällen bedeutet dies, dass die Abfrage eine WHERE <column_name> IS NOT NULL-Klausel enthalten muss, die der Vektorindexdefinition entspricht, es sei denn, die Spalte ist in der Tabellendefinition bereits als NOT NULL gekennzeichnet.

Eine ausführliche Liste der Einschränkungen finden Sie auf der Referenzseite für ungefähre Entfernungsfunktionen.

Beispiel

So suchen Sie nach den nächsten 100 Vektoren, die [1.0, 2.0, 3.0] entsprechen:

SELECT DocId
FROM Documents
WHERE WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

Wenn für die Einbettungsspalte Nullwerte zulässig sind:

SELECT DocId
FROM Documents
WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

Best Practices

Befolgen Sie diese Best Practices, um Ihre Vektorindexe zu optimieren und Abfrageergebnisse zu verbessern.

Optionen für die Vektorsuche abstimmen

Der optimale Wert für die Vektorsuche ist vom Anwendungsfall, vom Vektor-Dataset und von den Abfragevektoren abhängig. Möglicherweise müssen Sie eine iterative Abstimmung durchführen, um die besten Werte für Ihre spezifische Arbeitslast zu finden.

Hier sind einige hilfreiche Richtlinien, die Sie bei der Auswahl geeigneter Werte beachten sollten:

  • tree_depth (Baumebene): Wenn die indexierte Tabelle weniger als 10 Millionen Zeilen hat, verwenden Sie für tree_depth den Wert 2. Andernfalls unterstützt ein tree_depth von 3 Tabellen mit bis zu etwa 10 Milliarden Zeilen.

  • num_leaves: Verwendet die Quadratwurzel der Zeilenanzahl im Dataset. Ein höherer Wert kann die Erstellungszeit des Vektorindex erhöhen. num_leaves sollte nicht größer als table_row_count/1000 sein. Dies führt zu übermäßig kleinen Blättern und einer schlechten Leistung.

  • num_leaves_to_search: Diese Option gibt an, wie viele Blattknoten des Index durchsucht werden. Ein Erhöhen von num_leaves_to_search verbessert die Trefferquote, erhöht aber auch die Latenz und die Kosten. Als Wert für num_leaves_to_search empfehlen wir eine Zahl, die 1% der Gesamtzahl der Blätter entspricht, die in der CREATE VECTOR INDEX-Anweisung definiert sind. Wenn Sie eine Filterklausel verwenden, erhöhen Sie diesen Wert, um die Suche zu erweitern.

Wenn ein akzeptabler Recall erreicht wird, die Abfragekosten jedoch zu hoch sind und die maximale Anzahl von Abfragen pro Sekunde zu niedrig ist, versuchen Sie, num_leaves zu erhöhen. Gehen Sie dazu so vor:

  1. Legen Sie num_leaves auf einige mehrere k seines ursprünglichen Werts fest (z. B. 2 * sqrt(table_row_count)).
  2. Legen Sie für num_leaves_to_search das gleiche Vielfache der k-Werte des ursprünglichen Werts fest.
  3. Experimentieren Sie mit einer Reduzierung von num_leaves_to_search, um die Kosten und die Abfragen pro Sekunde zu senken und gleichzeitig die Recall-Funktion aufrechtzuerhalten.

Erinnerung verbessern

Es gibt mehrere Möglichkeiten für eine Verschlechterung der Erinnerung, darunter:

  • num_leaves_to_search ist zu klein: Bei einigen Abfragevektoren ist es möglicherweise schwieriger, die nächsten Nachbarn zu finden. Wenn Sie also num_leaves_to_search erhöhen, um nach mehr Blättern zu suchen, kann die Trefferquote verbessert werden. Die letzten Abfragen haben sich möglicherweise verschoben, um mehr dieser anspruchsvollen Vektoren zu enthalten.

  • Der Vektorindex muss neu erstellt werden: Die Baumstruktur des Vektorindex ist zum Zeitpunkt der Erstellung für den Datensatz optimiert und danach statisch. Wenn also nach dem Erstellen des ersten Vektorindex deutlich unterschiedliche Vektoren hinzugefügt werden, ist die Baumstruktur möglicherweise nicht optimal, was zu einer schlechten Trefferquote führt.

Vektorindex neu erstellen

So erstellen Sie Ihren Vektorindex ohne Ausfallzeit neu:

  1. Erstellen Sie einen neuen Vektorindex in derselben Einbettungsspalte wie der aktuelle Vektorindex und aktualisieren Sie die Parameter (z. B. OPTIONS) nach Bedarf.
  2. Verwenden Sie nach Abschluss der Indexerstellung den Hinweis FORCE_INDEX, um auf den neuen Index zu verweisen und die Vektorsuchabfrage zu aktualisieren. Dadurch wird sichergestellt, dass die Abfrage den neuen Vektorindex verwendet. Möglicherweise müssen Sie auch num_leaves_to_search in Ihrer neuen Abfrage anpassen.
  3. Löschen Sie den veralteten Vektorindex.

Nächste Schritte