pgvector兼容模式使用指南
pgvector是基于PostgreSQL數據庫的開源向量化數據的工具包。使用pgvector可以方便地構建向量索引,執行向量之間的相似度計算和查詢,常用于向量相似度搜索、推薦系統、聚類分析等場景。
前提條件
使用pgvector兼容模式前,請先滿足以下條件:
AnalyticDB PostgreSQL 6.0版實例的內核版本需為v6.6.1.0及以上。詳情請參見查看內核小版本。
AnalyticDB PostgreSQL 6.0版實例已開啟向量檢索引擎功能。詳情請參見開啟或關閉向量檢索引擎優化。
pgvector兼容模式說明
對于使用pgvector做向量檢索引擎的業務,云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫對pgvector的向量讀寫操作可以完全兼容,只需要修改索引構建的SQL語法即可。因此使用pgvector做向量檢索的業務可以完全無縫遷移到云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫中,基本不需要對業務側的代碼做改動。相比于原生的pgvector向量檢索引擎,云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫的pgvector兼容模式具有以下優勢:
基本上完全兼容pgvector的SQL語法,可以完全復用pgvector生態的客戶端。
pgvector目前主要在單機PostgreSQL數據庫上使用,而云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫是分布式版,能處理更大的向量數據量。
云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫使用自研的FastANN向量檢索引擎,具備比原生pgvector更優秀的性能,詳情請查看向量分析性能測試。
同時云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫在優化器和執行器層面打通了混合查詢計劃生成與混合查詢執行算子,具備非常完備的混合查詢能力,而原生pgvector基本不具備混合查詢的能力(只能通過分區等手段實現簡單的場景)。
云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫擁有Float2類型,可以對向量表存儲進行壓縮;也具備PQ量化能力,可以對向量索引存儲進行壓縮。因此云原生數據倉庫AnalyticDB PostgreSQL版向量數據庫相比于原生pgvector,也具有存儲成本的優勢。
創建向量表
創建兼容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;