pgvector是基於PostgreSQL資料庫的開源向量化資料的工具包。使用pgvector可以方便地構建向量索引,執行向量之間的相似性計算和查詢,常用於向量相似性搜尋、推薦系統、群集等情境。
前提條件
AnalyticDB PostgreSQL 6.0版執行個體的核心版本需為v6.6.1.0及以上。詳情請參見查看核心小版本。
AnalyticDB PostgreSQL 6.0版執行個體已開啟向量檢索引擎功能。詳情請參見開啟或關閉向量檢索引擎最佳化。
pgvector相容模式優勢
對於使用pgvector作為向量檢索引擎的業務,AnalyticDB for PostgreSQL向量資料庫完全相容pgvector的向量讀寫操作,只需修改索引構建的SQL文法即可。因此,使用pgvector的業務可以無縫遷移到pgvector相容模式中,幾乎不需要改動業務代碼。pgvector相容模式優勢如下:
無需安裝PGVector外掛程式。執行個體開啟向量檢索引擎最佳化後可以直接使用。
完全相容pgvector的SQL文法。pgvector相容模式可以直接複用pgvector生態的用戶端。
分布式架構。pgvector主要適用於單機PostgreSQL資料庫,而pgvector相容模式是分布式架構,能處理更大的向量資料量。
高效能向量檢索引擎。pgvector相容模式使用自研的FastANN向量檢索引擎,效能優於原生pgvector。
混合查詢能力。pgvector相容模式的最佳化器和執行器支援產生和執行混合查詢計劃,具備強大的混合查詢能力,而原生pgvector僅能通過分區等手段實現簡單的情境。
儲存成本優勢。pgvector相容模式支援Float2類型壓縮向量表格儲存體,並具備PQ量化能力壓縮向量索引儲存,從而降低儲存成本。
建立向量表
建立相容pgvector文法的向量表和雲原生資料倉儲AnalyticDB PostgreSQL版中原生向量表的文法相同,只是表中的向量列由數組(smallint[],float2[]和float4[])類型改為了vector類型(pgvector定義的向量類型),並且一個表可以支援多個向量列。
文法
CREATE TABLE [TABLE_NAME]
(
C1 DATATYPE,
C2 DATATYPE,
......,
CN VECTOR(DIM),
PRIMARY KEY(C1 [,C2,...CN])
) DISTRIBUTED BY(C1);其中CN為向量列,為vector類型,參數DIM為向量的維度。
樣本
建立一個命名為FACE_TABLE的向量表,其中C1為主鍵,C2和C3均為向量列,具體樣本如下:
CREATE TABLE FACE_TABLE (
C1 INT,
C2 VECTOR(512) NOT NULL,
C3 VECTOR(1536) NOT NULL,
C4 TIMESTAMP NOT NULL,
C5 VARCHAR(20) NOT NULL,
PRIMARY KEY (C1)
) DISTRIBUTED BY (C1);建立向量索引
雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫的pgvector相容模式下向量索引的建立文法,仍然採用FastANN向量檢索引擎的文法,與pgvector的原生文法規則不同,具體可以參考如下所示文法:
文法
CREATE INDEX [INDEX_NAME]
ON [SCHEMA_NAME].[TABLE_NAME]
USING ANN(COLUMN_NAME)
WITH (DIM=<DIMENSION>,
DISTANCEMEASURE=<MEASURE>,
HNSW_M=<M>,
HNSW_EF_CONSTRUCTION=<EF_CONSTURCTION>,
PQ_ENABLE=<PQ_ENABLE>,
PQ_SEGMENTS=<PQ_SEGMENTS>,
PQ_CENTERS=<PQ_CENTERS>,
EXTERNAL_STORAGE=<EXTERNAL_STORAGE>;由於pgvector的vector類型已經包含了維度資訊,建立索引中的DIM可以不填,其他參數可以查看原生向量索引的文法建立規則,詳情請參見建立向量索引。
樣本
在上文的FACE_TABLE的向量表上繼續建立索引,具體樣本如下:
-- 在向量列C2上建立三種向量索引。
CREATE INDEX idx_c2_l2 ON FACE_TABLE USING ann(C2) WITH (distancemeasure=l2, hnsw_m=64, pq_enable=1);
CREATE INDEX idx_c2_ip ON FACE_TABLE USING ann(C2) WITH (distancemeasure=ip, hnsw_m=64, pq_enable=1);
CREATE INDEX idx_c2_cosine ON FACE_TABLE USING ann(C2) WITH (distancemeasure=cosine, hnsw_m=64, pq_enable=1);
-- 在向量列C3上建立三種向量索引。
CREATE INDEX idx_c3_l2 ON FACE_TABLE USING ann(C3) WITH (distancemeasure=l2, hnsw_m=64, pq_enable=1);
CREATE INDEX idx_c3_ip ON FACE_TABLE USING ann(C3) WITH (distancemeasure=ip, hnsw_m=64, pq_enable=1);
CREATE INDEX idx_c3_cosine ON FACE_TABLE USING ann(C3) WITH (distancemeasure=cosine, hnsw_m=64, pq_enable=1);該樣本中列舉了在多個向量列上建立多個向量索引,在實際使用過程中需根據實際使用方式進行建立,以避免建立無效索引。
向量資料匯入
相容pgvector文法的向量表的向量資料匯入完全相容pgvector的文法。以上文的FACE_TABLE表為例說明INSERT的使用方法,具體樣本如下:
INSERT INTO FACE_TABLE
values (1, '[1,2,3 ... 512]', '[1,2,3 ... 1536]', '2023-12-29 00:00:00', 'aaa.bbb.ccc/face1.jpg');向量檢索
對於使用pgvector文法的向量表,向量檢索的文法和pgvector原生文法完全相容,可以直接使用pgvector的原生查詢方式進行查詢。
與向量檢索召回率相關的核心參數與雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫的原生參數一致,詳情請參見向量檢索。以FACE_TABLE為例說明向量檢查的具體使用方式:
-- 精確搜尋方式:按歐氏距離排序。
SELECT C1 FROM FACE_TABLE ORDER BY vector_l2_squared_distance(C2, '[1,2,3 ... 512]') LIMIT 10;
-- 精確搜尋方式:按內積距離排序。
SELECT C1 FROM FACE_TABLE ORDER BY vector_negative_inner_product(C2, '[1,2,3 ... 512]') LIMIT 10;
-- 精確搜尋方式:按餘弦相似性排序。
SELECT C1 FROM FACE_TABLE ORDER BY cosine_distance(C2, '[1,2,3 ... 512]') LIMIT 10;
-- 近似搜尋方式:按歐氏距離排序。
SELECT C1 FROM FACE_TABLE ORDER BY C2 <-> '[1,2,3 ... 512]' LIMIT 10;
-- 近似搜尋方式:按內積距離排序。
SELECT C1 FROM FACE_TABLE ORDER BY C2 <#> '[1,2,3 ... 512]' LIMIT 10;
-- 近似搜尋方式:按餘弦相似性排序。
SELECT C1 FROM FACE_TABLE ORDER BY C2 <=> '[1,2,3 ... 512]' LIMIT 10;由於已經在FACE_TABLE表的C2列上建了歐氏距離,內積距離和餘弦相似性三種距離度量的索引,因此樣本中的三種近似搜尋方式均能命中對應的向量索引。在實際使用的過程中,需要注意近似查詢方式中的操作符<->,<#>,<=>必須與向量索引的距離度量一一對應,否則如果沒有對應的距離度量的索引,將會退化為精確搜尋。
向量檢索的SQL最佳化
當需要返迴向量的距離score,您可以使用下面的SQL來提升效能。直接在向量索引的排序值基礎上進行計算得到最終的score,可以節省大量的計算耗時,具體樣本如下:
-- 按歐氏距離排序的向量檢索。 SELECT t.C1 as C1, sqrt(t.score) as score FROM (SELECT C1,C2 <-> '[1,2,3 ... 512]' as score FROM FACE_TABLE ORDER BY score LIMIT topk) t; -- 按內積距離排序的向量檢索。 SELECT t.C1 as C1, (-1 * t.score) as score FROM (SELECT C1, C2 <#> '[1,2,3 ... 512]' as score FROM FACE_TABLE ORDER BY score LIMIT topk) t; -- 按餘弦相似性排序的向量檢索。 SELECT t.C1 as C1, (1.0 - t.score) as score FROM (SELECT C1, C2 <=> '[1,2,3 ... 512]' as score FROM FACE_TABLE ORDER BY score LIMIT topk) t;向量檢索返回的結果中,score分別為歐氏距離(開方值),內積距離和餘弦相似性。
當需要根據score的範圍進行過濾並返回結果,您可以使用下面的SQL來實現。利用向量索引的排序值進行計算得到最終的score,可以節省大量的計算耗時,具體樣本如下:
-- 按歐氏距離排序的向量檢索。 SELECT t.C1 as C1, sqrt(t.score) as score FROM (SELECT C1,C2 <-> '[1,2,3 ... 512]' as score FROM FACE_TABLE ORDER BY score LIMIT topk) t WHERE score < 100; -- 按內積距離排序的向量檢索。 SELECT t.C1 as C1, (-1 * t.score) as score FROM (SELECT C1, C2 <#> '[1,2,3 ... 512]' as score FROM FACE_TABLE ORDER BY score LIMIT topk) t WHERE score > 10; -- 按餘弦相似性排序的向量檢索。 SELECT t.C1 as id, (1.0 - t.score) as score FROM (SELECT C1, C2 <=> '[1,2,3 ... 512]' as score FROM FACE_TABLE ORDER BY score LIMIT topk) t WHERE score > 0.5;
混合查詢
對於使用pgvector文法的向量表,混合查詢的方式與雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫的原生文法完全一致,詳情請參見混合檢索使用指南。下文以FACE_TABLE舉例說明混合查詢使用方法。
SELECT C1 FROM FACE_TABLE WHERE
C4 > '2023-10-01 00:00:00' AND C4 < '2024-01-01 00:00:00'
ORDER BY
C2 <-> '[1,2,3 ... 512]'
LIMIT 10;開原始碼庫適配
以開源大模型應用研發平台DIFY為例,介紹如何通過簡單的改造,讓使用pgvector的業務應用代碼能快速適配雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫。主要涉及三個方面的修改:
去除pgvector的外掛程式安裝。由於雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫並沒有適配pgvector外掛程式,只是在文法上相容了pgvector的向量表讀寫,因此不需要安裝pgvector的外掛程式。雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫在開通時會預設安裝自研向量檢索引擎外掛程式FastANN,只需要去掉pgvector的外掛程式即可。具體範例程式碼如下:
# 代碼位於api/core/rag/datasource/vdb/pgvector/pgvector.py def _create_collection(self, dimension: int): cache_key = f"vector_indexing_{self._collection_name}" lock_name = f"{cache_key}_lock" WITH redis_client.lock(lock_name, timeout=20): collection_exist_cache_key = f"vector_indexing_{self._collection_name}" if redis_client.get(collection_exist_cache_key): RETURN WITH self._get_cursor() AS cur: # 去除下面的pgvector外掛程式安裝 #cur.execute("CREATE EXTENSION IF NOT EXISTS vector") cur.execute(SQL_CREATE_TABLE.format(table_name=self.table_name, dimension=dimension)) # TODO: CREATE index https://github.com/pgvector/pgvector?tab=readme-ov-file#indexing redis_client.set(collection_exist_cache_key, 1, ex=3600)修改向量表建立。由於雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫是基於PostgreSQL9.4版本,不支援建表時使用using heap文法。雲原生資料倉儲AnalyticDB PostgreSQL版屬於分散式資料庫,需要設定分布鍵。修改向量表建立語句範例程式碼如下:
# 代碼位於api/core/rag/datasource/vdb/pgvector/pgvector.py SQL_CREATE_TABLE = """ CREATE TABLE IF NOT EXISTS {table_name} ( id uuid PRIMARY KEY, text text NOT NULL, meta jsonb NOT NULL, embedding vector({dimension}) NOT NULL ) USING heap; """ # 修改為如下代碼: SQL_CREATE_TABLE = """ CREATE TABLE IF NOT EXISTS {table_name} ( id uuid PRIMARY KEY, text text NOT NULL, meta jsonb NOT NULL, embedding vector({dimension}) NOT NULL ) DISTRIBUTED BY (id); """修改向量索引建立。由於雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫採用的是自研向量檢索引擎FastANN的向量能力,並不是直接移植的pgvector外掛程式,在向量索引的建立文法上是有區別的,尤其是索引關鍵詞是不同的。因此必須按照雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫的文法進行索引的建立。具體範例程式碼如下:
# 代碼位於api/core/rag/datasource/vdb/pgvector/pgvector.py # 添加建立向量索引的SQL(也可以直接在資料庫上建立索引,不再此添加) SQL_CREATE_INDEX = """ CREATE INDEX ON {table_name} USING ann(embedding) WITH (HNSW_M=16, HNSW_EF_CONSTRUCTION=500, PQ_ENABLE=1); """ def _create_collection(self, dimension: int): cache_key = f"vector_indexing_{self._collection_name}" lock_name = f"{cache_key}_lock" WITH redis_client.lock(lock_name, timeout=20): collection_exist_cache_key = f"vector_indexing_{self._collection_name}" if redis_client.get(collection_exist_cache_key): RETURN WITH self._get_cursor() AS cur: # 去除下面的pgvector外掛程式安裝 #cur.execute("CREATE EXTENSION IF NOT EXISTS vector") cur.execute(SQL_CREATE_TABLE.format(table_name=self.table_name, dimension=dimension)) # TODO: CREATE index https://github.com/pgvector/pgvector?tab=readme-ov-file#indexing # 建立ADB-PG向量資料庫的向量索引 cur.execute(SQL_CREATE_INDEX.format(table_name=self.table_name)) redis_client.set(collection_exist_cache_key, 1, ex=3600)
相關參考
pgvector多種語言的用戶端
語言 | 用戶端庫代碼地址 |
C | |
C++ | |
Go | |
Java,Kotlin,Groovy,Scala | |
PHP | |
Python | |
Rust |
pgvector生態的多種語言的用戶端均可以無縫接入到雲原生資料倉儲AnalyticDB PostgreSQL版向量資料庫,只需對向量索引相關的SQL進行修改即可。
支援的向量函數
函數作用 | 向量函數 | 傳回值類型 | 含義 | 支援的資料類型 |
計算 | l2_distance | double precision | 歐氏距離(開方值),通常用于衡量兩個向量的大小,表示兩個向量的距離。 | vector |
inner_product | double precision | 內積距離,在向量歸一化之後等於餘弦相似性,通常用於在向量歸一化之後替代餘弦相似性。 | vector | |
cosine_similarity | double precision | 餘弦相似性,取值範圍:[-1, 1],通常用于衡量兩個向量在方向上的相似性,而不關心兩個向量的實際長度。 | vector | |
vector_dims | integer | 計算單個向量的維度。 | vector | |
vector_norm | double precision | 計算單個向量的模長。 | vector | |
vector_add | vector | 兩個向量做加法。 | vector | |
vector_sub | vector | 兩個向量做減法。 | vector | |
vector_mul | vector | 兩個向量做乘法。 | vector | |
vector_angle | double precision | 計算兩個向量的夾角。 | vector | |
排序 | vector_l2_squared_distance | double precision | 歐氏距離(平方值),由於比歐氏距離(開方值)少了開方的計算,因此主要用於對歐氏距離(開方值)的排序邏輯,以減少計算量。 | vector |
vector_negative_inner_product | double precision | 反內積距離,為內積距離取反後的結果,主要用於對內積距離的排序邏輯,以保證排序結果按內積距離從大到小排序。 | vector | |
cosine_distance | double precision | 餘弦距離,取值範圍:[0, 2],主要用於對餘弦相似性的排序邏輯,以保證排序結果按餘弦相似性從大到小排序。 | vector |
表格中向量距離的計算公式詳情,請參見建立向量索引。
向量函數的使用樣本:
-- 歐氏距離
SELECT l2_distance('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 內積距離
SELECT inner_product('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 餘弦相似性
SELECT cosine_similarity('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 向量維度
SELECT vector_dims('[1,1,1,1]'::vector);
-- 向量模長
SELECT vector_norm('[1,1,1,1]'::vector);
-- 向量加法
SELECT vector_add('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 向量減法
SELECT vector_sub('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 向量乘法
SELECT vector_mul('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 向量夾角
SELECT vector_angle('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 歐氏平方距離
SELECT vector_l2_squared_distance('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 反內積距離
SELECT vector_negative_inner_product('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);
-- 餘弦距離
SELECT cosine_distance('[1,1,1,1]'::vector, '[2,2,2,2]'::vector);支援的向量操作符
操作符 | 計算含義 | 排序含義 | 支援的資料類型 |
<-> | 擷取歐氏距離(平方),結果等同於l2_squared_distance。 | 按歐氏距離(平方)從小到大排序。 | vector |
<#> | 擷取反內積,結果等同於negative_inner_product_distance。 | 按點積距離從大到小排序。 | vector |
<=> | 擷取餘弦距離,結果等同於cosine_distance。 | 按餘弦相似性從大到小排序。 | vector |
+ | 兩個向量的加法 | 無 | vector |
- | 兩個向量的減法 | 無 | vector |
* | 兩個向量的乘法 | 無 | vector |
向量操作符的使用樣本:
-- 歐氏距離(平方)
SELECT '[1,1,1,1]'::vector <-> '[2,2,2,2]'::vector AS score;
-- 反內積距離
SELECT '[1,1,1,1]'::vector <#> '[2,2,2,2]'::vector AS score;
-- 餘弦距離
SELECT '[1,1,1,1]'::vector <=> '[2,2,2,2]'::vector AS score;
-- 加法
SELECT '[1,1,1,1]'::vector + '[2,2,2,2]'::vector AS value;
-- 減法
SELECT '[1,1,1,1]'::vector - '[2,2,2,2]'::vector AS value;
-- 乘法
SELECT '[1,1,1,1]'::vector * '[2,2,2,2]'::vector AS value;