全部產品
Search
文件中心

AnalyticDB:AnalyticDB PostgreSQL稀疏向量使用指南

更新時間:Apr 09, 2025

當您需要高效儲存大部分元素為零的向量時,建立稀疏向量表比稠密向量表能節省大量的儲存空間。本文介紹雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫如何使用稀疏向量,包括建立稀疏向量表、構建稀疏向量索引、執行稀疏向量檢索,以及執行稀疏向量和稠密向量的混合查詢等。

背景簡介

在向量資料庫中,向量通常分為稠密向量(Dense)和稀疏向量(Sparse)。稀疏向量是一種大部分元素都是零的資料結構,通常有數萬個維度,但只有少數維度有實際值。這種資料結構特別適用於表示文本、映像等類型的資料。在關鍵詞檢索中,一個稀疏向量就表示一個文檔,其維度對應字典中的關鍵詞,值表示關鍵詞的重要性。如果使用BM25演算法產生稀疏向量,維度值包含關鍵詞匹配數量、詞頻和其他文本相關性因素。與普通數組或列表相比,稀疏向量能夠高效儲存和操作高維資料,顯著減少儲存空間和計算資源的消耗。這種資料結構在機器學習和自然語言處理中非常有用,適用於處理稀疏特徵的情境。

稠密向量和稀疏向量的區別如下圖所示:

image

前提條件

使用稀疏向量

建立稀疏向量表

文法

CREATE TABLE <SparseVectorTable> 
(  
    id int PRIMARY KEY,
    description text, 
    ...,
    sparse_vector svector(MAX_DIM), 
)DISTRIBUTED BY(id);

參數說明:

  • SparseVectorTable:向量表名稱。

  • sparse_vector:稀疏向量列,為svector類型。

  • MAX_DIM:稀疏向量的最大維度(非有值維度數量)。

使用樣本

建立一個名為svector_test的稀疏向量表,具體樣本如下:

-- 建立一個帶稀疏向量欄位的表
CREATE TABLE <svector_test>(  
    id bigint,
    type int,
    tag varchar(10),
    document text,
    sparse_features svector(250000)
) DISTRIBUTED BY (id);

-- 設定稀疏向量列為Plain模式
ALTER TABLE <svector_test> ALTER COLUMN sparse_features SET storage plain;
說明

Plain模式:在PostgreSQL中 ,儲存機制對於大對象、大欄位或無法容納在常規資料頁內的資料採用了一種名為Toast的策略。這種策略允許資料的高效儲存和訪問。當欄位使用Plain策略儲存時,資料被儲存為非壓縮和非分裂的一整塊,資料必須完全適應單個資料項目,不會溢出到Toast表中。Plain模式通常用於需要避免Toast處理開銷的小資料欄位,或者可以被高效儲存的資料類型,例如整數或小文字欄位。

建立稀疏向量索引

文法

稀疏向量的索引僅支援內積距離度量,不支援PQ量化和MMAP模式。文法規則如下:

CREATE INDEX <idx_sparse_vector>
ON MyTable USING vector_index (sparse_vector_column);
WITH (DISTANCEMEASURE=IP,
      HNSW_M=$M,
      HNSW_EF_CONSTRUCTION=$EF_CONSTURCTION);

參數說明:

  • idx_sparse_vector:向量索引名稱。

  • DISTANCEMEASURE:取值為IP,否則會出現參數報錯。

  • HNSW_M和HNSW_EF_CONSTRUCTION:參考建立向量索引

使用樣本

使用樣本中的svector_test稀疏向量表為例,建立稀疏向量索引,具體樣本如下:

-- 根據混合查詢的結構化欄位為其建立btree索引,如果結構化欄位為數群組類型,也可以添加gin索引
CREATE INDEX svector_test(type);
CREATE INDEX svector_test(tag);

-- 為稀疏向量列建立一個HNSW索引
CREATE INDEX ON svector_test USING ANN(sparse_features) WITH(DISTANCEMEASURE=IP,HNSW_M=64,pq_enable=0,external_storage=0);

稀疏向量資料的表示

雲原生資料倉儲AnalyticDB PostgreSQL版中,通過使用svector類型可以表示稀疏向量。svector類型支援以JSON格式的字串輸入,需要包含indices欄位和values欄位,分別表示下標數組(非負整數)和對應的值數組(浮點數)。一個20維的稀疏向量可以作如下資料表示:

稀疏向量資料樣本

[0, 0, 1.1, 0, 0, 0, 2.2, 0, 0, 3.3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

稀疏向量資料表示

{"indices":[2, 6, 9], "values":[1.1, 2.2, 3.3]}

參數說明:

  • indices:[2, 6, 9]表示稀疏向量中非0值的維度為第2,6,9維。

  • values:[1.1, 2.2, 3.3]表示對應的三個非0維度取值為1.1,2.2,3.3。

雲原生資料倉儲AnalyticDB PostgreSQL版資料庫中,使用svector可以將字串轉換為稀疏向量類型,SQL語句如下所示:

postgres=# SELECT '{"indices":[2, 6, 9], "values":[1.1, 2.2, 3.3]}'::svector;
svector
--------------------------------------------
{"indices":[2,6,9],"values":[1.1,2.2,3.3]}
(1 ROW)

稀疏向量資料的匯入

稀疏向量的資料匯入支援INSERT和COPY文法,以使用樣本中的svector_test稀疏向量表為例,介紹INSERT的使用方法,具體樣本如下:

-- 插入稀疏向量
INSERT INTO svector_test VALUES (1, 1, 'a', 'xxx', '{"indices":[2, 6, 9], "values":[1.1, 2.2, 3.3]}'::svector);
INSERT INTO svector_test VALUES (2, 2, 'b', 'xxx', '{"indices":[50, 100, 200], "values":[2.1, 3.2, 4.3]}'::svector);
INSERT INTO svector_test VALUES (3, 3, 'b', 'xxx', '{"indices":[150, 60, 90], "values":[5, 1e3, 1e-3]}'::svector);

稀疏向量的檢索

稀疏向量檢索分為精確檢索和近似檢索。

  • 精確檢索:完全按照相似性距離排序的暴力檢索。該方式需要比較每一個向量,因此檢索速度慢,但是召回率可以達到100%。該方法適用於對即時性要求不高,但對精確度要求高的情境。

  • 近似檢索:通過最佳化演算法快速找到近似的結果。雖然精確度有所降低,但檢索速度快。該方法適用於對即時性要求較高,但對精確度要求不嚴格的情境。

文法

對於稀疏向量表來說,稀疏向量檢索只支援內積距離度量的檢索方式。精確檢索和近似檢索的文法如下。

精確檢索

SELECT id, inner_product(<sparse_vector>, '{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector) 
AS score FROM <SparseVectorTable> ORDER BY negative_inner_product(<sparse_vector>, 
'{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector) LIMIT <topk>;

參數說明:

  • sparse_vector:向量列名稱。

  • SparseVectorTable:向量表名稱。

  • topk:需要檢索的結果集topk。

近似檢索

SELECT id, inner_product(<sparse_vector>, '{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector) 
AS score FROM <SparseVectorTable> ORDER BY <sparse_vector>
'{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector LIMIT <topk>;

參數說明:

  • sparse_vector:向量列名稱。

  • SparseVectorTable:向量表名稱。

  • topk:需要檢索的結果集topk。

使用樣本

使用樣本中的svector_test稀疏向量表為例,使用稀疏向量檢索的樣本如下:

稀疏向量檢索

SELECT id, sparse_features, 
'{"indices":[2,60,50], "values":[2,0.01, 0.02]}' <#> sparse_features AS score 
FROM svector_test
ORDER BY score
LIMIT 3;

檢索結果

   id   |                 sparse_features                 |        score
--------+-------------------------------------------------+---------------------
      3 | {"indices":[60,90,150],"values":[1000,0.001,5]} |                 -10
      1 | {"indices":[2,6,9],"values":[1.1,2.2,3.3]}      |   -2.20000004768372
      2 | {"indices":[50,100,200],"values":[2.1,3.2,4.3]} | -0.0419999957084656
(3 ROWS)
說明

稀疏向量的向量檢索召回率,可以通過fastann.sparse_hnsw_max_scan_points和fastann.sparse_hnsw_ef_search進行調節。具體內容,請參見相關參考

稠密向量與稀疏向量的混合查詢

建立一個同時包含稀疏向量和稠密向量的資料表HYBRID_SEARCH_TEST,並以此為基礎介紹如何?稠密向量和稀疏向量的混合查詢,具體建表SQL語句樣本如下:

-- 建立混合查詢的資料表
CREATE TABLE IF NOT EXISTS HYBRID_SEARCH_TEST (
   id integer PRIMARY key,
   corpus text,
   filter integer,
   dense_vector float4[],
   sparse_vector svector(250003)
) distributed BY (id);

-- 設定向量列為Plain模式
ALTER TABLE hybrid_search_test ALTER COLUMN dense_vector SET storage plain;
ALTER TABLE hybrid_search_test ALTER COLUMN sparse_vector SET storage plain;

建立索引的SQL語句樣本如下:

-- 建立結構化索引
CREATE INDEX ON hybrid_search_test(FILTER);

-- 建立稠密向量索引
CREATE INDEX ON hybrid_search_test USING ANN(dense_vector) WITH(DISTANCEMEASURE=IP, DIM=1024, HNSW_M=64, HNSW_EF_CONSTRUCTION=600, EXTERNAL_STORAGE=1);

-- 建立稀疏向量索引
CREATE INDEX ON hybrid_search_test USING ANN(sparse_vector) WITH(DISTANCEMEASURE=IP, HNSW_M=64, HNSW_EF_CONSTRUCTION=600);

稠密向量查詢、稀疏向量查詢以及混合查詢的SQL語句樣本如下:

-- 向量檢索 + filter
SELECT id, corpus FROM hybrid_search_test WHERE FILTER IN (0, 100, 200, 300, 400, 500, 600, 700, 800, 900) ORDER BY dense_vector <#> ARRAY[1,2,3,...,1024]::float4[] LIMIT 100;

-- sparse檢索 + filter
SELECT id, corpus FROM hybrid_search_test WHERE FILTER IN (0, 100, 200, 300, 400, 500, 600, 700, 800, 900) ORDER BY sparse_vector <#> '{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector LIMIT 100;
  
-- 混合檢索 + filter
WITH combined AS (
(SELECT id, corpus, inner_product_distance(dense_vector, ARRAY[1,2,3,...,1024]::float4[]) AS dist1, sparse_vector <#> '{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector AS dist2 FROM hybrid_search_test WHERE FILTER IN (0, 100, 200, 300, 400, 500, 600, 700, 800, 900) ORDER BY dist2 LIMIT 100)
  UNION ALL
(SELECT id, corpus, inner_product(sparse_vector, '{"indices":[1,2,..], "values":[1.1,2.2...]}'::svector) AS dist1, dense_vector <#> ARRAY[1,2,3,...,1024]::float4[] AS dist2 FROM hybrid_search_test WHERE FILTER IN (0, 100, 200, 300, 400, 500, 600, 700, 800, 900) ORDER BY dist2 LIMIT 100 )
) SELECT DISTINCT first_value(id) OVER (PARTITION BY id) AS id,  first_value(corpus) OVER (PARTITION BY id) AS corpus, MAX(dist1 - dist2) OVER (PARTITION BY id) AS dist FROM combined ORDER BY dist DESC LIMIT 100;

相關參考

稀疏向量檢索相關的核心參數

稀疏向量檢索相關的核心參數

功能說明

預設值

取值範圍

fastann.sparse_hnsw_max_scan_points

在HNSW索引中進行稀疏向量檢索時,最大掃描點個數,用於提前結束檢索,可用於召回率測試。

8000

[1, 8000000]

fastann.sparse_hnsw_ef_search

在HNSW索引中進行稀疏向量檢索時,檢索候選集大小,可用於召回率測試。

400

[10, 10000]

說明

上述核心參數可以在會話層級設定生效。

稀疏向量支援的向量函數

函數作用

向量函數

傳回值類型

含義

支援的資料類型

l2_distance

double_precision

歐氏距離(開方值),通常用于衡量兩個稀疏向量的大小,表示兩個稀疏向量的距離。

svector

計算

inner_product

double precision

內積距離,在向量歸一化之後等於餘弦相似性,通常用於在向量歸一化之後替代餘弦相似性。

svector

dp_distance

double precision

點積距離,和內積距離完全一致。

svector

cosine_similarity

double precision

餘弦相似性,取值範圍:[-1, 1],通常用于衡量兩個稀疏向量在方向上的相似性,而不關心兩個稀疏向量的實際長度。

svector

svector_add

svector

兩個稀疏向量的加法。

svector

svector_sub

svector

兩個稀疏向量的減法。

svector

svector_mul

svector

兩個稀疏向量的乘法。

svector

svector_norm

double precision

計算單個稀疏向量的模長。

svector

svector_angle

double precision

計算兩個稀疏向量的夾角。

svector

排序

l2_squared_distance

double precision

歐氏距離(平方值),由於比歐氏距離(開方值)少了開方的計算,因此主要用於對歐氏距離(開方值)的排序邏輯,以減少計算量。

svector

negative_inner_product

double precision

反內積距離,為內積距離取反後的結果,主要用於對內積距離的排序邏輯,以保證排序結果按內積距離從大到小排序。

svector

cosine_distance

double precision

餘弦距離,取值範圍:[0, 2],主要用於對餘弦相似性的排序邏輯,以保證排序結果按餘弦相似性從大到小排序。

svector

向量函數的使用樣本

-- 歐氏距離
SELECT l2_distance('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 內積距離
SELECT inner_product('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 餘弦相似性
SELECT l2_distance('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 向量加法
SELECT svector_add('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 向量減法
SELECT svector_sub('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 向量乘法
SELECT svector_mul('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 歐氏平方距離
SELECT l2_squared_distance('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 反內積距離
SELECT negative_inner_product('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);
-- 餘弦距離
SELECT cosine_distance('{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector, '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector);

稀疏向量支援的向量操作符

操作符

計算含義

排序含義

支援的資料類型

<->

擷取歐氏距離(平方),結果等同於l2_squared_distance。

svector

<#>

擷取反內積,結果等同於negative_inner_product

按點積距離從大到小排序。

svector

<#>

擷取餘弦距離,結果等同於cosine_distance。

svector

+

兩個稀疏向量的加法。

svector

-

兩個稀疏向量的減法。

svector

*

兩個稀疏向量的乘法。

svector

向量操作符樣本

-- 歐氏平方距離
SELECT '{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector <-> '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector AS score;

-- 反內積距離
SELECT '{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector <#> '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector AS score;

-- 餘弦距離
SELECT '{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector <=> '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector AS score;

-- 加法
SELECT '{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector + '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector AS value;

-- 減法
SELECT '{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector + '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector AS value;

-- 乘法
SELECT '{"indices":[1,2,3,4,5,6], "values":[1,2,3,4,5,6]}'::svector * '{"indices":[1,2,3,4,5,6], "values":[2,3,4,5,6,7]}'::svector AS value;