Vektorindexe erstellen und verwalten

Auf dieser Seite wird erläutert, wie Sie Spanner-Vektorindexe erstellen und verwalten. Dabei werden die Suche nach dem ungefähren nächsten Nachbarn (Approximate Nearest Neighbor, ANN) und baumbasierte Strukturen verwendet, um die Suche nach Vektorähnlichkeiten in Ihren Daten zu beschleunigen.

Spanner beschleunigt die ANN-Vektorsuche (Approximate Nearest Neighbor) mithilfe eines speziellen Vektorindex. Dieser Index nutzt den ScaNN-Algorithmus (Scalable Nearest Neighbor) von Google Research, einen hocheffizienten Algorithmus für die Suche nach dem nächsten Nachbarn.

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

  • Zweistufige Baumkonfiguration: Blattknoten (num_leaves) enthalten Gruppen von eng verwandten Vektoren zusammen mit dem entsprechenden Schwerpunkt. Die Stammebene besteht aus den Schwerpunkten aller Blattknoten.
  • Dreistufige Baumkonfiguration: Ähnlich wie ein zweistufiger Baum, aber mit einer zusätzlichen Verzweigungsebene (num_branches), von der aus die Schwerpunkte der Blattknoten 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 mit dem FORCE_INDEX Hinweis den am besten geeigneten Vektorindex für Ihren Anwendungsfall 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 Vorabaufteilung.

Vektorindex erstellen

So optimieren Sie die Trefferquote und Leistung eines Vektorindex:

  • 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.

  • Verwenden Sie für GoogleSQL die STORING Klausel und für PostgreSQL die INCLUDE Klausel, um eine Kopie einer Spalte im Vektorindex zu speichern. Wenn ein Spaltenwert im Vektorindex gespeichert ist, führt Spanner die Filterung 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- oder INCLUDE-Spalten, ermöglichen der Abfrage-Engine jedoch, die Filterung bei der Vektorsuche effizienter durchzuführen. Weitere Informationen finden Sie unter Vektorindex erstellen (GoogleSQL) oder Indexanweisungen (PostgreSQL).

Wenn Sie die Tabelle erstellen, muss die Einbettungsspalte ein Array des FLOAT32 (GoogleSQL) oder float4[] (PostgreSQL) Datentyps 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 der Arbeitslast, der Datasetgröße und den verfügbaren Rechenressourcen ab. Testen Sie verschiedene Dimensionen, um die kleinste Größe zu finden, die die Genauigkeit und Leistung für Ihre 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 gefüllt haben, können Sie einen Vektorindex für die Tabelle Documents mit einer Einbettungsspalte DocEmbedding mit der Kosinusdistanz erstellen:

GoogleSQL

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding)
  STORING (WordCount)
  OPTIONS (distance_type = 'COSINE');

PostgreSQL

CREATE INDEX doc_embedding_index
  ON documents
  USING scann(doc_embedding)
  INCLUDE (word_count)
  WITH (distance_type = 'COSINE')
  WHERE doc_embedding IS NOT NULL;

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 in der Vektorindexdefinition deklarieren. Dabei ist COLUMN_NAME der Name der Einbettungsspalte. So erstellen Sie eine Einbettungsspalte NullableDocEmbedding mit Nullwerten und Vektorindex mit der Kosinusdistanz:

GoogleSQL

CREATE VECTOR INDEX DocEmbeddingThreeLevelIndex
  ON Documents(NullableDocEmbedding)
  STORING (WordCount)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE');

PostgreSQL

CREATE INDEX doc_embedding_index
  ON documents
  USING scann(nullable_doc_embedding)
  INCLUDE (word_count)
  WITH (distance_type = 'COSINE')
  WHERE nullable_doc_embedding IS NOT NULL;

Spanner wählt jetzt automatisch optimierte Indexparameter (tree_depth, num_leaves und num_branches) für den Index basierend auf der Datasetgröße aus. Es ist nicht mehr erforderlich, diese Parameter explizit zu konfigurieren. Wenn Sie die Indexstruktur explizit konfigurieren müssen, können Sie diese Parameter in der Klausel SET OPTIONS angeben. Weitere Informationen finden Sie unter Best Practices für die Vektorindexierung.

Vektorindex filtern

Sie können auch einen gefilterten Vektorindex erstellen, um die ähnlichsten Elemente in der Datenbank zu finden, die der Filterbedingung entsprechen. Ein gefilterter Vektorindex indexiert selektiv Zeilen, die die angegebenen Filterbedingungen erfüllen, wodurch die Suchleistung verbessert wird.

Im folgenden Beispiel hat die Tabelle Documents2 eine Spalte mit dem Namen Category. Bei der Vektorsuche soll die Kategorie „Tech“ indexiert werden. Daher wird eine generierte Spalte erstellt, die NULL ergibt, 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,
  DocEmbeddi<ng ARRA>YFLOAT32(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)
);

Anschließend wird ein Vektorindex mit einem Filter erstellt. Der Vektorindex TechDocEmbeddingIndex indexiert nur Dokumente in der Kategorie „Tech“.

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')
  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. Die Abfrage durchsucht nur Dokumente in der Kategorie „Tech“. Sie können auch den Hinweis FORCE_INDEX verwenden (@{FORCE_INDEX=TechDocEmbeddingIndex} für GoogleSQL oder /*@ FORCE_INDEX = tech_doc_embedding_index */ für PostgreSQL), 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 Spalten ohne Einbettungsschlüssel in den Vektorindex aufnehmen. Dadurch kann die Abfrage-Engine die Filterung bei der Vektorsuche effizienter durchführen.

In der Anweisung zur Indexerstellung müssen Sie diese zusätzlichen Schlüsselspalten nach der Einbettungsspalte auflisten. Mit der folgenden Anweisung wird beispielsweise ein Vektorindex erstellt, der die Schlüsselspalten DocName und Author für eine effizientere Filterung 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')
  WHERE doc_embedding IS NOT NULL AND null_if_filtered IS NOT NULL;

Nächste Schritte