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;