Auf dieser Seite wird beschrieben, wie Sie ungefähre nächste Nachbarn (Approximate Nearest Neighbors, ANN) finden, Vektorindexe erstellen und Vektoreinbettungen mit den folgenden ANN-Distanzfunktionen in Spanner abfragen:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
Bei einem kleinen Datensatz können Sie K-Nearest Neighbors (KNN) verwenden, um die genauen k-nächsten Vektoren zu finden. Mit zunehmender Größe des Datensatzes steigen jedoch auch die Latenz und die Kosten einer KNN-Suche. Mit ANN können Sie die ungefähren k-nächsten Nachbarn mit deutlich geringerer Latenz und geringeren Kosten finden.
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 bei der ANN-Suche ungefähre Distanzen berechnet werden und möglicherweise nicht alle Vektoren im Datensatz berücksichtigt werden. Gelegentlich werden einige Vektoren zurückgegeben, die nicht zu den k nächsten Nachbarn gehören. Das wird als Recall-Verlust bezeichnet. Wie viel Trefferquotenverlust für Sie akzeptabel ist, hängt vom Anwendungsfall ab. In den meisten Fällen ist es jedoch ein akzeptabler Kompromiss, etwas Trefferquote im Gegenzug für eine verbesserte Datenbankleistung zu verlieren.
Weitere Informationen zu den ungefähren Distanzfunktionen von Spanner finden Sie unter:
APPROX_COSINE_DISTANCE
in GoogleSQLAPPROX_EUCLIDEAN_DISTANCE
in GoogleSQLAPPROX_DOT_PRODUCT
in GoogleSQL
Vektorindex
Cloud Spanner beschleunigt ANN-Vektorsuchen mithilfe eines speziellen Vektorindex. Dieser Index nutzt Scalable Nearest Neighbor (ScaNN) von Google Research, einen hocheffizienten Algorithmus für die Suche nach dem nächsten Nachbarn.
Der Vektorindex verwendet eine baumartige Struktur, um Daten zu partitionieren und schnellere Suchvorgänge zu ermöglichen. Spanner bietet sowohl zwei- als auch dreistufige Baumkonfigurationen:
- Konfiguration mit zwei Ebenen: Blattknoten (
num_leaves
) enthalten Gruppen von eng verwandten Vektoren zusammen mit dem entsprechenden Zentroid. Die Stammebene besteht aus den Schwerpunkten aller Blattknoten. - Baumkonfiguration mit drei Ebenen: Ähnlich wie bei einem Baum mit zwei Ebenen wird eine zusätzliche Ebene (
num_branches
) eingeführt, aus der die Schwerpunkte der Blattknoten weiter unterteilt 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 mit dem FORCE_INDEX
-Hinweis den am besten geeigneten Vektorindex für Ihren Anwendungsfall auswählen.
Weitere Informationen finden Sie unter VECTOR INDEX
-Anweisungen.
Beschränkungen
- Sie können Vektorindexe nicht vorab aufteilen. Weitere Informationen finden Sie unter Übersicht über das Aufteilen von Daten.
Vektorindex erstellen
Zur Optimierung des Rückrufs und der Leistung eines Vektorindex empfehlen wir Folgendes:
Erstellen Sie den Vektorindex, nachdem die meisten Zeilen mit Einbettungen in Ihre 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 Klausel
STORING
können Sie eine Kopie einer Spalte im Vektorindex speichern. Wenn ein Spaltenwert im Vektorindex gespeichert ist, führt Spanner das Filtern auf der Blattebene des Index aus, um die Abfrageleistung zu verbessern. Wir empfehlen, eine Spalte zu speichern, wenn sie in einer Filterbedingung verwendet wird. Weitere Informationen zur Verwendung vonSTORING
in einem Index finden Sie unter Index für reine Indexscans erstellen.
Wenn Sie die Tabelle erstellen, muss die Einbettungsspalte ein Array des Datentyps FLOAT32
(empfohlen) oder FLOAT64
sein und die Annotation vector_length enthalten, die die Dimension der Vektoren angibt.
Mit der folgenden DDL-Anweisung wird eine Tabelle Documents
mit einer Einbettungsspalte DocEmbedding
mit einer Vektorlänge erstellt:
CREATE TABLE Documents (
DocId INT64 NOT NULL,
...
DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);
Nachdem Sie die Tabelle Documents
mit Daten gefüllt haben, können Sie einen Vektorindex mit einem zweistufigen Baum und 1.000 Blattknoten für eine Tabelle Documents
mit einer Einbettungsspalte DocEmbedding
mit dem Kosinusabstand 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 einem dreistufigen Baum 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 Ihre Spalte für Einbettungen 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);
Vektorindex filtern
Sie können auch einen gefilterten Vektorindex erstellen, um die ähnlichsten Elemente in Ihrer Datenbank zu finden, die der Filterbedingung entsprechen. Bei einem gefilterten Vektorindex werden nur Zeilen indexiert, die die angegebenen Filterbedingungen erfüllen. Dadurch wird die Suchleistung verbessert.
Im folgenden Beispiel hat die Tabelle Documents
eine Spalte namens Category
.
Bei der Vektorsuche soll die Kategorie „Tech“ indexiert werden. Daher erstellen wir eine generierte Spalte, die NULL ergibt, wenn die Kategoriebedingung nicht erfüllt ist.
CREATE TABLE Documents (
DocId INT64 NOT NULL,
Category STRING(MAX),
NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);
Anschließend erstellen wir einen Vektorindex mit einem Filter. Im TechDocEmbeddingIndex
-Vektorindex werden nur Dokumente in der Kategorie „Tech“ indexiert.
CREATE VECTOR INDEX TechDocEmbeddingIndex
ON Documents(DocEmbedding)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
Wenn Spanner die folgende Abfrage ausführt, die Filter enthält, die mit TechDocEmbeddingIndex
übereinstimmen, wird TechDocEmbeddingIndex
automatisch ausgewählt und beschleunigt. Mit der Abfrage werden nur Dokumente in der Kategorie „Tech“ durchsucht. Sie können auch {@FORCE_INDEX=TechDocEmbeddingIndex}
verwenden, um Spanner zu zwingen, TechDocEmbeddingIndex
explizit zu verwenden.
SELECT *
FROM Documents
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;
Vektoreinbettungen abfragen
Wenn Sie einen Vektorindex abfragen möchten, verwenden Sie eine der drei Näherungsfunktionen für die Distanz:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
Für die Verwendung der Näherungsfunktionen für die Distanz gelten die folgenden Einschränkungen:
- Die Funktion für die ungefähre Distanz muss die Distanz zwischen einer Spalte mit Einbettungen und einem konstanten Ausdruck (z. B. einem Parameter oder einem Literal) berechnen.
- Die Ausgabe der Funktion für die ungefähre Distanz muss in einer
ORDER BY
-Klausel als einziger Sortierschlüssel verwendet werden und nach derORDER BY
muss eineLIMIT
angegeben werden. - In der Abfrage müssen Zeilen, die nicht indexiert sind, explizit herausgefiltert werden. In den meisten Fällen muss die Abfrage eine
WHERE <column_name> IS NOT NULL
-Klausel enthalten, die der Vektorindexdefinition entspricht, es sei denn, die Spalte ist in der Tabellendefinition bereits alsNOT NULL
gekennzeichnet.
Eine detaillierte Liste der Einschränkungen finden Sie auf der Referenzseite zur Funktion für die ungefähre Distanz.
Beispiele
So suchen Sie nach den 100 Vektoren, die [1.0, 2.0, 3.0]
am nächsten sind:
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 die Spalte für Einbettungen Nullwerte enthalten kann:
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
Mit den folgenden Best Practices können Sie Ihre Vektorindexe optimieren und die Suchergebnisse verbessern.
Optionen für die Vektorsuche anpassen
Der optimale Wert für die Vektorsuche hängt vom Anwendungsfall, dem Vektordatensatz und den Anfragevektoren ab. Möglicherweise müssen Sie iterative Anpassungen vornehmen, um die besten Werte für Ihre spezifische Arbeitslast zu finden.
Hier sind einige hilfreiche Richtlinien für die Auswahl geeigneter Werte:
tree_depth
(Baumebene): Wenn die zu indexierende Tabelle weniger als 10 Millionen Zeilen hat, verwenden Sie fürtree_depth
den Wert2
. Andernfalls unterstützt eintree_depth
von3
Tabellen mit bis zu etwa 10 Milliarden Zeilen.num_leaves
: Verwenden Sie die Quadratwurzel der Anzahl der Zeilen im Dataset. Ein größerer Wert kann die Build-Zeit des Vektorindex verlängern. Vermeiden Sie es,num_leaves
größer alstable_row_count/1000
festzulegen, da dies zu zu kleinen Blättern und einer schlechten Leistung führt.num_leaves_to_search
: Mit dieser Option wird angegeben, wie viele Blattknoten des Index durchsucht werden. Durch Erhöhen vonnum_leaves_to_search
wird der Recall verbessert, aber auch die Latenz und die Kosten steigen. Wir empfehlen, fürnum_leaves_to_search
eine Zahl zu verwenden, die 1% der Gesamtzahl der Blätter in derCREATE VECTOR INDEX
-Anweisung entspricht. Wenn Sie eine Filterklausel verwenden, erhöhen Sie diesen Wert, um die Suche auszuweiten.
Wenn ein akzeptabler Recall erreicht wird, die Kosten für Abfragen jedoch zu hoch sind, was zu einer niedrigen maximalen QPS führt, versuchen Sie, num_leaves
zu erhöhen. Gehen Sie dazu so vor:
- Setzen Sie
num_leaves
auf ein Vielfaches k des ursprünglichen Werts (z. B.2 * sqrt(table_row_count)
). - Legen Sie
num_leaves_to_search
als das k-fache des ursprünglichen Werts fest. - Reduzieren Sie
num_leaves_to_search
, um Kosten und QPS zu verbessern und gleichzeitig den Recall beizubehalten.
Erinnerung verbessern
Es gibt mehrere Möglichkeiten, wie sich der Recall verschlechtern kann, z. B.:
num_leaves_to_search
ist zu klein: Möglicherweise ist es schwieriger, die nächsten Nachbarn für einige Anfragevektoren zu finden. Wenn Sienum_leaves_to_search
erhöhen, um mehr Blätter zu durchsuchen, kann die Trefferquote verbessert werden. Bei den letzten Anfragen sind möglicherweise mehr dieser anspruchsvollen Vektoren enthalten.Vektorindex muss neu erstellt werden: Die Baumstruktur des Vektorindex wird zum Zeitpunkt der Erstellung für das Dataset optimiert und ist danach statisch. Wenn nach dem Erstellen des ursprünglichen Vektorindex deutlich unterschiedliche Vektoren hinzugefügt werden, ist die Baumstruktur möglicherweise suboptimal, was zu einem schlechteren Recall führt.
Vektorindex neu erstellen
So erstellen Sie Ihren Vektorindex ohne Ausfallzeiten neu:
- Erstellen Sie einen neuen Vektorindex für dieselbe Einbettungsspalte wie den aktuellen Vektorindex und aktualisieren Sie die Parameter (z. B.
OPTIONS
) entsprechend. - Nachdem der Index erstellt wurde, verwenden Sie den
FORCE_INDEX
-Hinweis, um auf den neuen Index zu verweisen und die Vektorsuchanfrage zu aktualisieren. So wird sichergestellt, dass für die Abfrage der neue Vektorindex verwendet wird. Möglicherweise müssen Sienum_leaves_to_search
in Ihrer neuen Abfrage neu abstimmen. - Löschen Sie den veralteten Vektorindex.
Nächste Schritte
Weitere Informationen zu
VECTOR INDEX
-Anweisungen in GoogleSQLErste Schritte mit der Spanner-Vektorsuche enthält ein Schritt-für-Schritt-Beispiel für die Verwendung von ANN.