建立 ScaNN 索引

選取文件版本:

使用儲存的嵌入項目,透過 AlloyDB Omni 產生 ScaNN 向量索引和查詢嵌入項目。

ScaNN 索引是 Google 製作的樹狀結構量化索引,用於近似最鄰近搜尋。與 HNSW 相比,這項技術可縮短索引建構時間,並減少記憶體用量。此外,根據工作負載,與 HNSW 相比,這項功能可提供更快的 QPS。

事前準備

開始建立索引前,請先完成下列必要條件。

建立自動調整的索引

自動調整的 ScaNN 索引可讓 AlloyDB Omni 管理及調整索引結構,簡化索引建立作業。如需精細控管索引調整作業,請建立手動調整的 ScaNN 索引

您可以透過兩種方式最佳化自動調整的索引:

  • (預設) 向量搜尋召回率和延遲時間,但索引建構時間較長
  • 在索引建構時間和搜尋效能之間取得平衡

如要建立自動調整的 ScaNN 索引,請執行下列指令。

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)

更改下列內容:

  • INDEX_NAME:要建立的索引名稱。例如,my_scann_index。索引名稱會在資料庫中共用。確認每個索引名稱在資料庫的每個資料表中都是唯一的。

  • TABLE:要新增索引的資料表。

  • EMBEDDING_COLUMN:儲存 vector 資料的資料欄。

  • DISTANCE_FUNCTION: 要用於這個索引的距離函式。選擇下列其中一個選項:

    • L2 距離:l2

    • 點積:dot_product

    • 餘弦距離:cosine

這項指令會建立專為搜尋效能最佳化的 ScaNN 索引。如要變更這項設定,請執行下列指令:

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (MODE='AUTO',
      OPTIMIZATION='OPTIMIZATION')

更改下列內容:

  • INDEX_NAME:要建立的索引名稱。例如,my_scann_index。索引名稱會在資料庫中共用。確認每個索引名稱在資料庫的每個資料表中都是唯一的。

  • TABLE:要新增索引的資料表。

  • EMBEDDING_COLUMN:儲存 vector 資料的資料欄。

  • DISTANCE_FUNCTION: 要用於這個索引的距離函式。選擇下列其中一個選項:

    • L2 距離:l2

    • 點積:dot_product

    • 餘弦距離:cosine

  • (選用) OPTIMIZATION: 設為下列其中一項:

    • (預設) SEARCH_OPTIMIZED:同時最佳化向量搜尋召回率和向量搜尋延遲時間,但索引建構時間會較長。

    • BALANCED:平衡索引建構時間和搜尋效能。

    如果設定 OPTIMIZATION,則必須一併提供 MODE='AUTO'

建立手動調整的索引

如果應用程式對回呼和索引建構時間有特定要求,您可以手動建立及調整 ScaNN 索引。

如要為含有儲存向量嵌入內容的資料欄手動建立 ScaNN 索引,請參閱下列指令。

兩層樹狀結構索引

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER);
  • INDEX_NAME:要建立的索引名稱。例如:my_scann_index。資料庫會共用索引名稱。請確保資料庫中每個資料表的索引名稱都不重複。
  • TABLE:要新增索引的資料表。
  • EMBEDDING_COLUMN:儲存 `vector` 資料的資料欄。
  • DISTANCE_FUNCTION:要用於這個索引的距離函式。選擇下列其中一個選項:
    • L2 距離:l2
    • 點積:dot_product
    • 餘弦距離:cosine
  • NUM_LEAVES_VALUE: 要套用至這個索引的分區數量。設定為介於 1 到 3,000 萬之間的任何值。如要進一步瞭解如何選擇這個值,請參閱「調整 ScaNN 索引」。
  • QUANTIZER:要使用的量化器類型。請注意,ScaNN 索引可以載入資料欄引擎,進一步加快向量搜尋速度。選擇下列其中一個選項:
    • (預設) SQ8:在查詢效能與召回率之間取得平衡。這通常不到 1 到 2%。
    • (預先發布版) AH:與 SQ8 相比,非對稱式雜湊 (AH) 最多可壓縮 4 倍。如果啟用資料欄引擎,且索引和表格資料已填入資料欄引擎,則查詢效能可能會有所提升。詳情請參閱「調整 ScaNN 的最佳做法」。
    • FLAT:提供最高召回率 (99% 以上),但會影響搜尋效能。

三層樹狀結構索引

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 2);
  • INDEX_NAME:要建立的索引名稱。例如:my_scann_index。資料庫會共用索引名稱。請確保資料庫中每個資料表的索引名稱都不重複。
  • TABLE:要新增索引的資料表。
  • EMBEDDING_COLUMN:儲存 `vector` 資料的資料欄。
  • DISTANCE_FUNCTION:要用於這個索引的距離函式。選擇下列其中一個選項:
    • L2 距離:l2
    • 點積:dot_product
    • 餘弦距離:cosine
  • NUM_LEAVES_VALUE: 要套用至這個索引的分區數量。設定為介於 1 到 3,000 萬之間的任何值。如要進一步瞭解如何選擇這個值,請參閱「調整 ScaNN 索引」。
  • QUANTIZER:要使用的量化器類型。請注意,ScaNN 索引可以載入資料欄引擎,進一步加快向量搜尋速度。選擇下列其中一個選項:
    • (預設) SQ8:在查詢效能與召回率之間取得平衡。這通常不到 1 到 2%。
    • (預先發布版) AH:與 SQ8 相比,非對稱式雜湊 (AH) 最多可壓縮 4 倍。如果啟用資料欄引擎,且索引和表格資料已填入資料欄引擎,則查詢效能可能會有所提升。詳情請參閱「調整 ScaNN 的最佳做法」。
    • FLAT:提供最高召回率 (99% 以上),但會影響搜尋效能。
  • (選用) AUTO_MAINTENANCE: 控制是否啟用或停用索引的自動維護功能。如要進一步瞭解自動維護作業,請參閱「維護向量索引」。
    • (預設) ON:AlloyDB Omni 會自動維護索引。
    • OFF:AlloyDB Omni 不會對索引執行自動維護作業。
  • max_num_levels = 2:K-means 分群樹的群集中心層級數量上限。將這個參數設為 2,即可建立三層索引。

四層樹狀結構索引

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      max_num_levels = 3);
  • INDEX_NAME:要建立的索引名稱。例如:my_scann_index。資料庫會共用索引名稱。請確保資料庫中每個資料表的索引名稱都不重複。
  • TABLE:要新增索引的資料表。
  • EMBEDDING_COLUMN:儲存 `vector` 資料的資料欄。
  • DISTANCE_FUNCTION:要用於這個索引的距離函式。選擇下列其中一個選項:
    • L2 距離:l2
    • 點積:dot_product
    • 餘弦距離:cosine
  • NUM_LEAVES_VALUE: 要套用至這個索引的分區數量。設定為介於 1 到 3,000 萬之間的任何值。如要進一步瞭解如何選擇這個值,請參閱「調整 ScaNN 索引」。
  • QUANTIZER:要使用的量化器類型。請注意,ScaNN 索引可以載入資料欄引擎,進一步加快向量搜尋速度。選擇下列其中一個選項:
    • (預設) SQ8:在查詢效能與召回率之間取得平衡。這通常不到 1 到 2%。
    • 預先發布版 AH:與 SQ8 相比,非對稱雜湊 (AH) 最多可壓縮 4 倍。如果啟用資料欄引擎,且索引和表格資料已填入資料欄引擎,則查詢效能可能會有所提升。詳情請參閱「調整 ScaNN 的最佳做法」。
    • FLAT:提供最高召回率 (99% 以上),但會影響搜尋效能。
  • max_num_levels = 3:K-means 分群樹的群集中心層級數量上限。將這個參數設為 3,即可建立四層索引。

將手動調整的索引轉換為自動調整的索引

如要將手動調整的索引轉換為自動調整的索引,請完成下列步驟:

  1. 重設為手動調整的索引定義的所有查詢參數。

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    請替換下列變數:

    • INDEX_NAME:要轉換的索引名稱。例如,my_scann_index。資料庫會共用索引名稱。確認資料庫中每個資料表的索引名稱都不重複。

    • PARAMETER_NAME:以半形逗號分隔的清單,內含要重設的查詢參數名稱。例如:num_leaves, quantization

      請注意,您必須先重設所有其他查詢參數,才能重設 num_leaves

  2. 重新建立手動調整的索引,將其轉換為自動調整的索引。

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

real[] 資料類型建立 ScaNN 索引

如要為使用 real[] 資料類型 (而非 vector) 的嵌入資料欄建立索引,請將資料欄轉換為 vector 資料類型:

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)

更改下列內容:

  • INDEX_NAME:要建立的索引名稱。例如,my_scann_index。索引名稱會在資料庫中共用。確認每個索引名稱在資料庫的每個資料表中都是唯一的。

  • TABLE:要新增索引的資料表。

  • DIMENSIONS:模型支援的維度數量。

  • EMBEDDING_COLUMN:儲存 vector 資料的資料欄。

  • DISTANCE_FUNCTION: 要用於這個索引的距離函式。選擇下列其中一個選項:

    • L2 距離:l2

    • 點積:dot_product

    • 餘弦距離:cosine

查看建立索引的進度

如要查看索引建立進度,請使用 pg_stat_progress_create_index 檢視畫面:

SELECT * FROM pg_stat_progress_create_index;

「狀態」phase欄會顯示索引建立作業的目前狀態。索引建構階段完成後,索引的資料列就不會顯示。

為空白資料表或資料列不足的資料表建立延後索引

根據預設,您無法在空白資料表或列數少於 num_leaves 索引選項值的資料表上建立 ScaNN 索引。

如要略過這項限制,請啟用延後建立索引功能,讓 AlloyDB Omni 延後建立索引,直到資料表中的資料列數量達到 num_leaves 定義的門檻為止。達到門檻後,AlloyDB Omni 就會在背景開始建構索引。

這項延遲作業是非封鎖程序,可讓讀取和寫入等其他資料庫作業持續進行,不會中斷。由於索引重建作業會在背景執行,因此如果資料表是以小批次方式擷取資料列,就適合使用延後建立索引功能。當列數達到門檻時,系統會自動觸發索引重建作業。

不過,如果您打算在單一交易中將大量資料列插入資料表,建議您將交易分割為多筆交易,或產生 ScaNN 索引,但不要啟用延後建立索引。

啟用延後建立索引

如要啟用延後建立索引功能,請按照下列步驟操作:

  1. 確認已啟用 scann.enable_index_maintenance 標記和下列其中一個標記:

    如要設定用量,請使用 gcloud CLI:

    • 如要啟用延遲建立索引功能和其他預先發布版功能,請按照下列步驟操作:

      gcloud alloydb instances update INSTANCE_ID \
         --database-flags scann.enable_index_maintenance=on \
         --database-flags scann.enable_preview_features=on \
         --region=REGION_ID \
         --cluster=CLUSTER_ID \
         --project=PROJECT_ID
      
    • 如要明確啟用延後建立索引功能,但不要啟用其他搶先版功能,請執行下列步驟:

      gcloud alloydb instances update INSTANCE_ID \
         --database-flags scann.enable_index_maintenance=on \
         --database-flags scann.enable_index_with_insufficient_data=on \
         --region=REGION_ID \
         --cluster=CLUSTER_ID \
         --project=PROJECT_ID
      

    更改下列內容:

    • INSTANCE_ID:執行個體的 ID。
    • REGION_ID:執行個體所在的區域,例如 us-central1
    • CLUSTER_ID:執行個體所在的叢集 ID。
    • PROJECT_ID:叢集所在的專案 ID。
  2. 建立 ScaNN 索引。如果您在手動模式中建立索引,請確認 auto_maintenance 參數已設為 on。詳情請參閱「建立手動調整的索引」。

限制

  • 自動建立索引的背景程序會使用資料庫層級的旗標值。即使您使用 SET LOCAL 指令設定任何工作階段層級的旗標,程序仍會考量資料庫層級設定的旗標值。
  • 如果您打算在單一交易中,將大量資料大量插入空白資料表,建議您執行單一插入交易,然後建立 ScaNN 索引。

強制在空白或小型資料表上建立索引

AlloyDB Omni 會使用驗證機制,防止在空白資料表或資料列極少的資料表上建立 ScaNN 索引,原因如下:

  • ScaNN 索引訓練的資料不足。這可能會導致向量相似度搜尋的召回率不佳。

  • 寫入資料庫的效能可能會降低。

如果效能不佳,建議您延後建立索引

不過,在某些開發或測試情境中,您可能需要在空白或小型資料表上建立索引。在這種情況下,您可以強制建立索引。請注意,強制建立索引需要 SUPERUSER 權限。

如要強制建立索引,請完成下列步驟:

  1. 在資料庫中,將 scann.allow_blocked_operations 工作階段層級參數設為 true

    SET scann.allow_blocked_operations = true;
    
  2. 如果您用來執行這些查詢的使用者沒有 SUPERUSER 權限,請指派權限:

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    請替換下列變數:

    • USERNAME:要授予 SUPERUSER 權限的使用者名稱。
    • PASSWORD:使用者的密碼。

平行建構索引

為加快建立索引的速度,AlloyDB Omni 可能會視資料集和所選索引類型,自動產生多個平行工作站。建立三或四層 ScaNN 索引時,或資料集超過 1 億列時,通常會觸發這項限制。

雖然 AlloyDB Omni 會自動調整平行工作站數量,但您可以使用下列 PostgreSQL 查詢規劃參數調整平行工作站:

為避免建立 ScaNN 索引時發生記憶體不足問題,請確保 maintenance_work_memshared_buffers 資料庫旗標的值小於機器總記憶體。

執行查詢

將嵌入內容儲存到資料庫並建立索引後,即可開始查詢資料。您無法使用 alloydb_scann 擴充功能執行大量搜尋查詢。

如要找出文字字串最接近的語意鄰近項目,可以使用 google_ml.embedding() 函式將文字轉換為向量。

由於 google_ml.embedding() 會傳回實際陣列,因此您必須先將函式呼叫明確轉換為 vector,再套用至其中一個最鄰近鄰居運算子,例如 L2 距離的 <->。這些運算子隨後可以使用 ScaNN 索引,找出語意最相似的嵌入資料庫資料列。

SELECT * FROM TABLE
ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY
  google_ml.embedding(
      model_id => 'MODEL_ID',
      content => 'CONTENT')::vector
LIMIT ROW_COUNT

請替換下列變數:

  • TABLE:包含要與文字比較的嵌入內容的資料表。

  • EMBEDDING_COLUMN: 包含所儲存嵌入的資料欄。

  • DISTANCE_FUNCTION_QUERY:要用於這項查詢的距離函式。建立索引時,請選擇距離函式的對等查詢:

    • L2 距離:<->

    • 內積:<#>

    • 餘弦距離:<=>

  • MODEL_ID:要使用的已註冊嵌入模型 ID。

  • CONTENT:要翻譯成嵌入內容並搜尋的文字字串。

  • ROW_COUNT:要傳回的資料列數。舉例來說,如要取得單一最佳結果,請指定 1

後續步驟