Membuat dan mengelola indeks vektor

Halaman ini menjelaskan cara membuat dan mengelola indeks vektor Spanner, yang menggunakan penelusuran perkiraan tetangga terdekat (ANN) dan struktur berbasis pohon untuk mempercepat penelusuran kesamaan vektor pada data Anda.

Spanner mempercepat penelusuran vektor approximate nearest neighbor (ANN) dengan menggunakan indeks vektor khusus. Indeks ini memanfaatkan Scalable Nearest Neighbor (ScaNN) dari Google Research, yaitu algoritma tetangga terdekat yang sangat efisien.

Indeks vektor menggunakan struktur berbasis pohon untuk mempartisi data dan memfasilitasi penelusuran yang lebih cepat. Spanner menawarkan konfigurasi pohon dua tingkat dan tiga tingkat:

  • Konfigurasi pohon dua tingkat: Node daun (num_leaves) berisi grup vektor yang terkait erat beserta centroid yang sesuai. Level root terdiri dari sentroid dari semua node daun.
  • Konfigurasi pohon tiga tingkat: Mirip dengan konsep pohon dua tingkat, sekaligus memperkenalkan lapisan cabang tambahan (num_branches), yang sentroid node daunnya dipartisi lebih lanjut untuk membentuk tingkat root (num_leaves).

Spanner memilih indeks untuk Anda. Namun, jika Anda tahu bahwa indeks tertentu berfungsi paling baik, Anda dapat menggunakan petunjuk FORCE_INDEX untuk memilih menggunakan indeks vektor yang paling sesuai untuk kasus penggunaan Anda.

Untuk mengetahui informasi selengkapnya, lihat Pernyataan VECTOR INDEX untuk GoogleSQL dan Pernyataan INDEX untuk PostgreSQL.

Batasan

  • Anda tidak dapat memisahkan indeks vektor sebelumnya. Untuk mengetahui informasi selengkapnya, lihat Ringkasan pra-pemisahan.

Membuat indeks vektor

Untuk mengoptimalkan recall dan performa indeks vektor, sebaiknya:

  • Buat indeks vektor Anda setelah sebagian besar baris dengan embedding ditulis ke database Anda. Anda mungkin juga perlu membangun ulang indeks vektor secara berkala setelah menyisipkan data baru. Untuk mengetahui informasi selengkapnya, lihat Membangun ulang indeks vektor.

  • Untuk GoogleSQL, gunakan klausa STORING dan untuk PostgreSQL, gunakan klausa INCLUDE untuk menyimpan salinan kolom dalam indeks vektor. Jika nilai kolom disimpan dalam indeks vektor, Spanner akan melakukan pemfilteran di tingkat leaf indeks untuk meningkatkan performa kueri. Sebaiknya simpan kolom jika digunakan dalam kondisi pemfilteran.

  • Gunakan kolom kunci non-embedding dalam indeks vektor. Kolom kunci mirip dengan kolom STORING atau INCLUDE, tetapi memungkinkan mesin kueri melakukan pemfilteran secara lebih efisien selama penelusuran vektor. Untuk mengetahui informasi selengkapnya, lihat Membuat indeks vektor (GoogleSQL) atau Pernyataan indeks (PostgreSQL).

Saat Anda membuat tabel, kolom embedding harus berupa array jenis data FLOAT32 (GoogleSQL) atau float4[] (PostgreSQL) (direkomendasikan), dan memiliki anotasi panjang vektor (vector_length=>N untuk GoogleSQL atau VECTOR LENGTH N untuk PostgreSQL), yang menunjukkan dimensi vektor.

Panjang vektor optimal bergantung pada beban kerja, ukuran set data, dan sumber daya komputasi yang tersedia. Bereksperimenlah dengan berbagai dimensi untuk menemukan ukuran terkecil yang mempertahankan akurasi dan performa aplikasi Anda.

Pernyataan DDL berikut membuat tabel Documents dengan kolom penyematan DocEmbedding dengan panjang vektor:

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)
);

Setelah mengisi tabel Documents, Anda dapat membuat indeks vektor dengan pohon dua tingkat dan 1.000 node daun pada tabel Documents dengan kolom embedding DocEmbedding menggunakan jarak kosinus:

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;

Jika kolom sematan Anda tidak ditandai sebagai NOT NULL dalam definisi tabel, Anda harus mendeklarasikannya dengan klausa WHERE COLUMN_NAME IS NOT NULL dalam definisi indeks vektor, dengan COLUMN_NAME adalah nama kolom sematan Anda. Untuk membuat indeks vektor dengan hierarki tiga tingkat dan 1000000 node leaf pada kolom embedding yang dapat bernilai null NullableDocEmbedding menggunakan jarak kosinus:

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;

Memfilter indeks vektor

Anda juga dapat membuat indeks vektor yang difilter untuk menemukan item yang paling mirip dalam database Anda yang cocok dengan kondisi filter. Indeks vektor yang difilter mengindeks baris secara selektif yang memenuhi kondisi filter yang ditentukan, sehingga meningkatkan performa penelusuran.

Dalam contoh berikut, tabel Documents2 memiliki kolom bernama Category. Dalam penelusuran vektor, kita ingin mengindeks kategori "Teknologi", jadi kita membuat kolom turunan yang dievaluasi menjadi NULL jika kondisi kategori tidak terpenuhi.

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)
);

Kemudian, kita membuat indeks vektor dengan filter. Indeks vektor TechDocEmbeddingIndex hanya mengindeks dokumen dalam kategori "Teknologi".

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;

Saat menjalankan kueri berikut, yang memiliki filter yang cocok dengan TechDocEmbeddingIndex, Spanner akan otomatis memilih dan dipercepat oleh TechDocEmbeddingIndex. Kueri hanya menelusuri dokumen dalam kategori "Teknologi". Anda juga dapat menggunakan petunjuk FORCE_INDEX (@{FORCE_INDEX=TechDocEmbeddingIndex} untuk GoogleSQL atau /*@ FORCE_INDEX = tech_doc_embedding_index */ untuk PostgreSQL) untuk memaksa Spanner menggunakan indeks secara eksplisit.

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;

Untuk meningkatkan performa kueri, Anda dapat menyertakan kolom kunci non-embedding dalam indeks vektor. Hal ini memungkinkan mesin kueri melakukan pemfilteran dengan lebih efisien selama penelusuran vektor.

Dalam pernyataan pembuatan indeks, Anda harus mencantumkan kolom kunci tambahan ini setelah kolom penyematan. Misalnya, pernyataan berikut membuat indeks vektor yang menyertakan kolom kunci DocName dan Author untuk pemfilteran yang lebih efisien:

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;

Langkah berikutnya