當您需要高效儲存大部分元素為零的向量時,建立稀疏向量表比稠密向量表能節省大量的儲存空間。本文介紹雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫如何使用稀疏向量,包括建立稀疏向量表、構建稀疏向量索引、執行稀疏向量檢索,以及執行稀疏向量和稠密向量的混合查詢等。
背景簡介
在向量資料庫中,向量通常分為稠密向量(Dense)和稀疏向量(Sparse)。稀疏向量是一種大部分元素都是零的資料結構,通常有數萬個維度,但只有少數維度有實際值。這種資料結構特別適用於表示文本、映像等類型的資料。在關鍵詞檢索中,一個稀疏向量就表示一個文檔,其維度對應字典中的關鍵詞,值表示關鍵詞的重要性。如果使用BM25演算法產生稀疏向量,維度值包含關鍵詞匹配數量、詞頻和其他文本相關性因素。與普通數組或列表相比,稀疏向量能夠高效儲存和操作高維資料,顯著減少儲存空間和計算資源的消耗。這種資料結構在機器學習和自然語言處理中非常有用,適用於處理稀疏特徵的情境。
稠密向量和稀疏向量的區別如下圖所示:

前提條件
已建立雲原生資料倉儲AnalyticDB PostgreSQL版v6.6.2.3及以上版本執行個體。具體操作,請參見建立執行個體。
已開啟向量檢索引擎功能,請參見開啟或關閉向量檢索引擎最佳化。
使用稀疏向量
建立稀疏向量表
文法
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;