建立及管理向量索引

本頁面說明如何建立及管理 Spanner 向量索引,這類索引會使用近似近鄰 (ANN) 搜尋和樹狀結構,加快資料的向量相似度搜尋速度。

Spanner 會使用專屬向量索引,加快近似最鄰近項目 (ANN) 向量搜尋的速度。這項索引採用 Google 研究團隊的可擴充最近鄰 (ScaNN),這是一種高效率的最近鄰演算法。

向量索引會使用樹狀結構分割資料,加快搜尋速度。Spanner 提供兩層和三層樹狀結構設定:

  • 兩層樹狀結構設定:葉節點 (num_leaves) 包含密切相關的向量群組,以及對應的群集中心。根層級包含所有葉節點的質心。
  • 三層樹狀結構設定:概念與兩層樹狀結構類似,但會導入額外的分支層 (num_branches),葉節點群集中心會進一步分割,形成根層級 (num_leaves)。

Spanner 會為您選擇索引。不過,如果您知道特定索引最合適,可以使用 FORCE_INDEX 提示,選擇最適合您用途的向量索引。

詳情請參閱 GoogleSQL 的 VECTOR INDEX 陳述式和 PostgreSQL 的 INDEX 陳述式

限制

建立向量索引

為提升向量索引的召回率和效能,建議您:

  • 將大部分含有嵌入的資料列寫入資料庫後,請建立向量索引。插入新資料後,您可能也需要定期重建向量索引。詳情請參閱「重建向量索引」。

  • 如果是 GoogleSQL,請使用 STORING 子句;如果是 PostgreSQL,請使用 INCLUDE 子句,將資料欄副本儲存在向量索引中。如果資料欄值儲存在向量索引中,Spanner 會在索引的葉節點層級執行篩選,以提升查詢效能。如果資料欄用於篩選條件,建議您儲存該資料欄。

  • 在向量索引中使用非嵌入鍵資料欄。主要資料欄與 STORINGINCLUDE 資料欄類似,但可讓查詢引擎在向量搜尋期間更有效率地執行篩選作業。詳情請參閱「建立向量索引」(GoogleSQL) 或「索引陳述式」(PostgreSQL)。

建立資料表時,嵌入資料欄必須是 FLOAT32 (GoogleSQL) 或 float4[] (PostgreSQL) 資料類型 (建議使用),且具有向量長度註解 (GoogleSQL 為 vector_length=>N,PostgreSQL 為 VECTOR LENGTH N),表示向量的維度。

最佳向量長度取決於工作負載、資料集大小和可用的運算資源。嘗試使用不同維度,找出能維持應用程式準確率和效能的最小尺寸。

下列 DDL 陳述式會建立 Documents 資料表,並包含具有向量長度的嵌入資料欄 DocEmbedding

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

填入 Documents 資料表後,您可以在 Documents 資料表上建立向量索引,其中包含嵌入欄 DocEmbedding,並使用餘弦距離,建立雙層樹狀結構和 1000 個葉節點:

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;

如果資料表定義中未將嵌入資料欄標示為 NOT NULL,您必須在向量索引定義中,使用 WHERE COLUMN_NAME IS NOT NULL 子句宣告該資料欄,其中 COLUMN_NAME 是嵌入資料欄的名稱。如要在可為空值的嵌入資料欄 NullableDocEmbedding 上,使用餘弦距離建立具有三層樹狀結構和 1000000 個葉節點的向量索引,請執行下列操作:

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;

篩選向量索引

您也可以建立經過篩選的向量索引,在資料庫中找出符合篩選條件的最相似項目。經過篩選的向量索引會選擇性地為符合指定篩選條件的資料列建立索引,進而提升搜尋效能。

在下列範例中,資料表 Documents2 有一個名為 Category 的資料欄。在向量搜尋中,我們想為「科技」類別建立索引,因此建立的產生資料欄會評估類別條件是否符合,如果不符合,則評估為 NULL

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

接著,我們建立含有篩選器的向量索引。TechDocEmbeddingIndex 向量索引只會為「技術」類別中的文件建立索引。

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;

當 Spanner 執行下列查詢時,由於查詢的篩選條件符合 TechDocEmbeddingIndex,系統會自動選取 TechDocEmbeddingIndex 並加速執行查詢。這項查詢只會搜尋「科技」類別的文件。您也可以使用 FORCE_INDEX 提示 (GoogleSQL 為 @{FORCE_INDEX=TechDocEmbeddingIndex},PostgreSQL 為 /*@ FORCE_INDEX = tech_doc_embedding_index */),強制 Spanner 明確使用索引。

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;

如要提升查詢效能,可以在向量索引中加入非嵌入式索引鍵資料欄。這樣查詢引擎就能在向量搜尋期間更有效率地執行篩選作業。

在索引建立陳述式中,您必須在嵌入欄後列出這些額外鍵欄。舉例來說,下列陳述式會建立向量索引,其中包含 DocNameAuthor 鍵欄,可提高篩選效率:

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;

後續步驟