Auf dieser Seite wird erläutert, wie Sie Spanner-Vektorindexe erstellen und verwalten, die die Suche nach dem ungefähren nächsten Nachbarn (Approximate Nearest Neighbor, ANN) und baumbasierte Strukturen verwenden, um die Suche nach Vektorähnlichkeiten in Ihren Daten zu beschleunigen.
Spanner beschleunigt die Vektorsuche nach dem ungefähren nächsten Nachbarn (Approximate Nearest Neighbor, ANN) 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. Cloud Spanner bietet sowohl zwei- als auch dreistufige Baumkonfigurationen:
- Konfiguration mit zweistufigem Baum: Blattknoten (
num_leaves) enthalten Gruppen von eng verwandten Vektoren zusammen mit dem entsprechenden Zentroid. Die Stammebene besteht aus den Schwerpunkten aller Blattknoten. - Dreistufige Baumkonfiguration: Ähnlich wie bei einem zweistufigen Baum wird eine zusätzliche Ebene mit Zweigen (
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 für Ihren Anwendungsfall am besten geeigneten Vektorindex auswählen.
Weitere Informationen finden Sie unter VECTOR INDEX-Anweisungen für GoogleSQL und INDEX-Anweisungen für PostgreSQL.
Beschränkungen
- Vektorindexe können nicht vorab aufgeteilt werden. Weitere Informationen finden Sie unter Übersicht über das Aufteilen von Daten.
Vektorindex erstellen
Um den Recall und die Leistung eines Vektorindex zu optimieren, 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.
Verwenden Sie für GoogleSQL die Klausel
STORINGund für PostgreSQL die KlauselINCLUDE, um eine Kopie einer Spalte im Vektorindex zu 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.Verwenden Sie Spalten ohne Einbettungsschlüssel im Vektorindex. Schlüsselspalten ähneln
STORING- oderINCLUDE-Spalten, ermöglichen es der Abfrage-Engine jedoch, Filter während der Vektorsuche effizienter auszuführen. Weitere Informationen finden Sie unter Vektorindex erstellen (GoogleSQL) oder Indexanweisungen (PostgreSQL).
Wenn Sie die Tabelle erstellen, muss die Spalte für Einbettungen ein Array des Datentyps FLOAT32 (GoogleSQL) oder float4[] (PostgreSQL) sein (empfohlen) und eine Anmerkung zur Vektorlänge (vector_length=>N für GoogleSQL oder VECTOR LENGTH N für PostgreSQL) enthalten, die die Dimension der Vektoren angibt.
Die optimale Vektorlänge hängt von Ihrer Arbeitslast, der Datasetgröße und den verfügbaren Rechenressourcen ab. Experimentieren Sie mit verschiedenen Dimensionen, um die kleinste Größe zu finden, die die Genauigkeit und Leistung Ihrer Anwendung beibehält.
Mit der folgenden DDL-Anweisung wird eine Tabelle Documents mit einer Einbettungsspalte DocEmbedding mit einer Vektorlänge erstellt:
GoogleSQL
CREATE TABLE Documents (
UserId INT64 NOT NULL,
DocId INT64 NOT NULL,
Author STRING (1024),
DocContents Bytes(MAX),
DocEmbedding ARRAY<FLOAT32>(vector_length=>128) NOT NULL,
NullableDocEmbedding ARRAY<FLOAT32>(vector_length=>128),
WordCount INT64
) PRIMARY KEY (DocId);
PostgreSQL
CREATE TABLE documents (
user_id bigint not null,
doc_id bigint not null,
author varchar(1024),
doc_contents bytea,
doc_embedding float4[] VECTOR LENGTH 128 not null,
nullable_doc_embedding float4[] VECTOR LENGTH 128,
word_count bigint,
PRIMARY KEY (doc_id)
);
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 die Tabelle Documents mit der Einbettungsspalte DocEmbedding erstellen. Verwenden Sie dazu die Kosinusdistanz:
GoogleSQL
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
STORING (WordCount)
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
PostgreSQL
CREATE INDEX doc_embedding_index
ON documents
USING scann(doc_embedding)
INCLUDE (word_count)
WITH (distance_type = 'COSINE', num_leaves = 1000)
WHERE doc_embedding IS NOT NULL;
Wenn Ihre Einbettungsspalte in der Tabellendefinition nicht als NOT NULL gekennzeichnet ist, müssen Sie sie mit einer WHERE COLUMN_NAME IS NOT NULL-Klausel in der Vektorindexdefinition deklarieren. Dabei ist COLUMN_NAME der Name Ihrer Einbettungsspalte. So erstellen Sie einen Vektorindex mit einem dreistufigen Baum und 1.000.000 Blattknoten für die Spalte NullableDocEmbedding mit Nullwerten für Einbettungen mit dem Kosinusabstand:
GoogleSQL
CREATE VECTOR INDEX DocEmbeddingThreeLevelIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);
PostgreSQL
CREATE INDEX doc_embedding_index
ON documents
USING scann(nullable_doc_embedding)
INCLUDE (word_count)
WITH (distance_type = 'COSINE', tree_depth = 3, num_branches = 1000, num_leaves = 1000000)
WHERE nullable_doc_embedding IS NOT NULL;
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 Documents2 eine Spalte namens Category.
In unserer Vektorsuche möchten wir die Kategorie „Tech“ indexieren. Dazu erstellen wir eine generierte Spalte, die den Wert NULL hat, wenn die Kategoriebedingung nicht erfüllt ist.
GoogleSQL
CREATE TABLE Documents2 (
UserId INT64 NOT NULL,
DocId INT64 NOT NULL,
DocName STRING (1024),
Author STRING (1024),
DocContents Bytes(MAX),
Category STRING(MAX),
NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
DocEmbedding ARRAY<FLOAT32>(vector_length=>128)
) PRIMARY KEY (DocId);
PostgreSQL
CREATE TABLE documents2 (
user_id bigint not null,
doc_id bigint not null,
doc_name varchar(1024),
author varchar(1024),
doc_contents bytea,
category varchar,
null_if_filtered boolean GENERATED ALWAYS AS (CASE WHEN category = 'Tech' THEN true END) VIRTUAL HIDDEN,
doc_embedding float4[] VECTOR LENGTH 128,
PRIMARY KEY (doc_id)
);
Als Nächstes erstellen wir einen Vektorindex mit einem Filter. Im TechDocEmbeddingIndex-Vektorindex werden nur Dokumente in der Kategorie „Tech“ indexiert.
GoogleSQL
CREATE VECTOR INDEX TechDocEmbeddingIndex
ON Documents2(DocEmbedding)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
PostgreSQL
CREATE INDEX tech_doc_embedding_index
ON documents2
USING scann(doc_embedding)
INCLUDE (null_if_filtered)
WITH (distance_type = 'COSINE', num_leaves = 1000)
WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL;
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 den Hinweis FORCE_INDEX (@{FORCE_INDEX=TechDocEmbeddingIndex} für GoogleSQL oder /*@ FORCE_INDEX = tech_doc_embedding_index */ für PostgreSQL) verwenden, um Spanner zu zwingen, den Index explizit zu verwenden.
GoogleSQL
SELECT *
FROM Documents2
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;
PostgreSQL
SELECT *
FROM documents2
WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL
ORDER BY spanner.approx_cosine_distance(doc_embedding, ARRAY[1.0::float4, 2.0::float4, 3.0::float4])
LIMIT 10;
Um die Abfrageleistung zu verbessern, können Sie Schlüsselspalten, die keine Einbettungen sind, in Ihren Vektorindex aufnehmen. So kann die Abfrage-Engine Filterung bei der Vektorsuche effizienter durchführen.
In der Anweisung zum Erstellen des Index müssen Sie diese zusätzlichen Schlüsselspalten nach der Spalte für die Einbettung auflisten. Mit der folgenden Anweisung wird beispielsweise ein Vektorindex erstellt, der die Schlüsselspalten DocName und Author für ein effizienteres Filtern enthält:
GoogleSQL
CREATE VECTOR INDEX DocEmbeddingIndexWithKeys
ON Documents2(DocEmbedding, DocName, Author)
STORING(NullIfFiltered)
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
OPTIONS (...);
PostgreSQL
CREATE INDEX doc_embedding_index_with_keys
ON documents2
USING scann(doc_embedding, doc_name, author)
INCLUDE (null_if_filtered)
WITH (distance_type = 'COSINE', num_leaves = 1000)
WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL;
Nächste Schritte
Weitere Informationen zu ungefähren nächsten Nachbarn in Spanner
Weitere Informationen zu Indexanweisungen für GoogleSQL
VECTOR INDEXund PostgreSQLINDEX