向量检索

功能介绍

人工智能算法可以对物理世界的人/物/场景所产生各种非结构化数据(如语音、图片、视频,语言文字、行为等)进行抽象,变成多维的向量。

这些向量如同数学空间中的坐标,标识着各个实体和实体关系。我们一般将非结构化数据变成向量的过程称为 Embedding,而非结构化检索则是对这些生成的向量进行检索,从而找到相应实体的过程。

传统的关键词搜索主要依赖于对关键词的匹配,而忽略了查询的含义和语境。但语义搜索的优点在于它可以更好地满足用户的意图,尤其是对于复杂的查询和问题。

语义搜索能够:

  • 理解查询的上下文
  • 处理模糊或不完整的查询
  • 提供更相关和有用的搜索结果

目前在业务上对向量检索能力进行支持,业界的使用上通常有如下几种:

  1. 基于开源的向量检索内核(比如faiss/scann/proxima/nmslib等)自己来做服务化,包成一个Restful API形式的在线向量检索服务

  2. 选用专门的向量检索数据库来支持向量检索的场景

  3. 选用一个数据库/数仓产品,包含向量检索功能的支持

在绝大多数情况下,使用专用向量数据库的弊都要远远大于利:

  • 数据冗余
  • 大量不必要的数据搬运工作
  • 分布式组件之间缺乏一致性
  • 额外的专业技能带来的复杂度成本、学习成本、以及人力成本
  • 额外的软件许可费用
  • 极其有限的查询语言能力、可编程性、可扩展性
  • 有限的工具链
  • 与真正数据库相比更差的数据完整性和可用性

而且在一个分布式的数据库产品里支持向量检索有个好处,可以利用其分布式的能力,可以轻松应对海量向量数据的场景。在支持分布式事务的产品里,对向量的写入、更新和删除同样天然满足分布式事务的强一致性。

ProtonBase 提供向量检索的能力,支持两种索引类型IVFFlat(Inverted File with Flat Quantization)HNSW(Hierarchical Navigable Small World),实现了高效的向量检索的召回。支持数据的批量导入,实时更新/删除,并且支持条件过滤和向量检索的联合查询。

  • IVFFlat是一种基于倒排索引的近似最近邻搜索算法,用于高效地查询向量之间的相似度。它将向量空间分为若干个划分区域,每个区域都包含一些向量,并创建倒排索引,用于快速地查找与给定向量相似的向量。IVFFlat构建索引速度更快,消耗内存更少,但查询性能略慢。更适合在处理超大数据集时,可以显著减少搜索空间,提高查询效率。

  • HNSW是一种基于图的索引结构,构建了一个多层次的导航图。HNSW使用了随机跳跃的方式进行邻近搜索,通过在不同层的图结构中搜索,快速找到相似向量。它消耗内存更多,构建索引更慢,但查询性能更好。在高维数据中表现出更好的查询性能和准确性。

聚类索引 (IVFFlat)图索引(HNSW)
数据集大小[100K, ~][~, 100M]
召回精度
查询性能
构建速度
内存消耗

索引类型选择指南

在选择索引类型时,需要根据具体的使用场景和需求来决定。以下是选择 HNSW 和 IVFFlat 索引的建议:

何时选择 HNSW

HNSW 索引是大多数场景下的推荐选择,特别是当:

  1. 对查询精度要求高:HNSW 在高维数据中表现出更好的查询性能和准确性,适合对搜索结果质量要求较高的场景。

  2. 数据集规模适中:HNSW 适合处理百万级到千万级的向量数据集。

  3. 查询延迟敏感:HNSW 的查询性能优于 IVFFlat,适合对响应时间有严格要求的应用。

  4. 有足够的内存资源:HNSW 消耗更多内存,但能提供更好的查询性能。

何时选择 IVFFlat

IVFFlat 索引适合以下场景:

  1. 超大数据集:当数据量超过一亿条时,IVFFlat 能显著减少搜索空间,提高查询效率。

  2. 内存资源有限:IVFFlat 消耗内存较少,适合资源受限的环境。

  3. 可以接受中等精度:如果对查询精度要求不是特别高,IVFFlat 是一个经济高效的选择。

  4. 索引构建时间敏感:IVFFlat 构建索引速度更快,适合需要频繁重建索引的场景。

推荐选择

我们建议用户首先选择 HNSW 索引,因为它在大多数情况下能提供更好的查询性能和准确性。只有在内存资源非常有限或数据集特别庞大(超过一亿条记录)时,才考虑使用 IVFFlat 索引。

ProtonBase 通过实现PostgreSQL生态的pgvector插件进行扩展来支持向量检索的能力,pgvector 有着优雅简单易用的接口,更是继承了PostgreSQL生态的超能力集合。

建表语句

向量数据类型为vector,最高支持16000维,在声明向量字段的时候需要同时显示标识出其维度信息,内部是个Float类型的数组,这些数组被组成成紧凑的二进制格式以便高效的存储和检索。

CREATE TABLE items
(
    id        bigserial PRIMARY KEY,
    embedding vector(3) -- 定义维度
)
    USING columnar;

向量索引

目前距离的度量方式支持三种:

  • <-> - 欧式距离(L2 Distance),索引参数 vector_l2_ops

  • <#> - 内积距离(Inner Product),索引参数 vector_ip_ops

  • <=> - 余弦距离(Cosine Distance),索引参数 vector_cosine_ops

根据向量索引类型的不同,在构建的时候可以支持不同的向量索引。

离线批量导入与索引优化实践

在实际业务中,常见的场景是需要离线批量导入大量向量数据。此时,推荐采用如下流程以显著提升数据导入和索引构建的效率:

  1. 先删除已有索引(DROP INDEX):避免每条数据写入时都实时维护索引,提升导入速度。
  2. 批量导入数据:使用 COPY 或批量 INSERT 等方式高效导入。
  3. 执行 VACUUM:导入完成后,执行 VACUUM,整理表空间,提升后续索引构建效率。
  4. 重建索引(CREATE INDEX):最后统一创建向量索引,此时索引构建速度远高于逐条维护。

这种流程不仅能大幅提升批量导入的速度,还能让索引的构建和后续检索效率更高。适用于数据初始导入、离线数据迁移、周期性全量刷新等场景。

示例流程:

-- 1. 删除已有索引(如有)
DROP INDEX IF EXISTS idx_items_embedding;
 
-- 2. 批量导入数据
COPY items (embedding) FROM '/path/to/data.csv' WITH (FORMAT csv);
 
-- 3. 整理表空间
VACUUM items;
 
-- 4. 创建索引
CREATE INDEX idx_items_embedding ON items USING split_ivfflat (embedding vector_l2_ops) WITH (lists = 100);

建议:对于实时写入或小批量数据导入场景,则无需此优化流程,直接维护索引即可。

创建 IVFFlat 聚类索引

参数说明:

  • lists: 聚类的中心点个数。lists数值的选择一般规则为:小于100万行时,lists = rows/1000,大于100万行时,lists = sqrt(rows),能获得比较好的聚类效果。
-- 创建欧式距离索引
CREATE INDEX ON items USING split_ivfflat (embedding vector_l2_ops) WITH (lists = 100);
-- 创建内聚距离索引
CREATE INDEX ON items USING split_ivfflat (embedding vector_ip_ops) WITH (lists = 100);
-- 创建余弦距离索引
CREATE INDEX ON items USING split_ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
 
--导入向量数据
INSERT INTO items (embedding) VALUES ('[1.0,0.0,-1.0]');

创建 HNSW 图索引

参数说明(参数之间可以灵活组合):

  • m:节点最大邻居(出度)数量。该参数影响图的召回精度和性能,邻居上限越多,图的连通性会更好,但会影响写入和查询性能,默认值 16。

  • ef_construction:节点候选邻居数量,基于其选择最终的 m 个邻居。该参数影响图的召回精度和性能,候选邻居越多,裁边算法得到合法的邻居数量越多,主要是影响构图性能,默认值 64。

  • quantizer: 用于向量量化的参数,有两个选项,int8 和 fp16,通过将原始浮点向量转换为更紧凑的类型表示,减少类型占用位数来压缩数据,可以显著影响索引的性能和内存使用。使用量化参数适合内存受限的环境,或者数据集太大无法放入内存,或者对精度要求不严格的场景。量化会引入一定的精度损失,量化后的索引查询速度可能比非量化版本稍慢(因为需要解量化)。

-- 创建欧式距离索引
CREATE INDEX ON items USING split_hnsw (embedding vector_l2_ops) WITH (m=16, ef_construction = 64);
-- 创建内聚距离索引
CREATE INDEX ON items USING split_hnsw (embedding vector_ip_ops) WITH (m=16, ef_construction = 64);
-- 创建余弦距离索引
CREATE INDEX ON items USING split_hnsw (embedding vector_cosine_ops) WITH (m=16, ef_construction = 64);
 
-- 使用 int8 量化参数压缩
CREATE INDEX ON items USING split_hnsw (embedding vector_l2_ops) WITH (quantizer = 'int8');
-- 使用 fp16 量化参数压缩
CREATE INDEX ON items USING split_hnsw (embedding vector_l2_ops) WITH (quantizer = "fp16");
 
--导入向量数据
INSERT INTO items (embedding) VALUES ('[1.0,0.0,-1.0]');

注:如果不配置索引,会使用暴力检索的方式进行向量检索。

向量量化方式的选择

HNSW 索引支持两种向量量化方式,int8 和 fp16。通过以下表格选择合适的方式。

特性int8fp16
原理将原始 float32 向量转换为 8-bit 整数表示将原始 float32 向量转换为 16-bit 浮点表示
内存减少75% (32→8 bit)50% (32→16 bit)
精度损失较大较小
计算复杂度较高(需量化/反量化)低(直接类型转换)
适用场景内存敏感型,可以接受一定精度的损失,大规模数据集精度敏感型,需要较好平衡内存和精度,GPU 支持 fp16 优化

查询方式

参数说明:

  • ivfflat.probes:设置考察聚类中心点的个数。该值越大,召回率越高,检索性能越差。当该值等于构建index时指定的lists个数是,退化为暴力检索

基于 IVFFlat 索引的查询

-- 全局生效配置
alter system set ivfflat.probes=10;
 
-- 当前session生效配置,优先级高于全局配置
SET ivfflat.probes = 10;
 
SELECT *
FROM items
ORDER BY embedding <-> '[3,1,2]'
LIMIT 5;

基于 HNSW 索引的查询

参数说明:

  • hnsw.ef_search: 控制候选队列的长度。该参数可以控制搜索精度,ef_search 越大,查询过程越难收敛,考察的节点越多,召回精度通常越高。
SET hnsw.ef_search = 100;
 
SELECT *
FROM items
ORDER BY embedding <-> '[3,1,2]'
LIMIT 5;

量化索引查询方式

当使用量化索引(int8 或 fp16)时,查询方式需要使用专门的函数来计算距离,而不是使用操作符:

  • l2_distance(vector, vector) - 计算两个向量之间的欧氏距离(L2 Distance)
  • inner_product(vector, vector) - 计算两个向量之间的内积距离(Inner Product)
  • cosine_distance(vector, vector) - 计算两个向量之间的余弦距离(Cosine Distance)

使用示例:

-- 使用 l2_distance 查询量化索引
SET hnsw.ef_search = 100;
 
SELECT *
FROM items
ORDER BY l2_distance(embedding, '[3,1,2]')
LIMIT 5;
 
-- 使用 inner_product 查询量化索引
SELECT *
FROM items
ORDER BY inner_product(embedding, '[3,1,2]')
LIMIT 5;
 
-- 使用 cosine_distance 查询量化索引
SELECT *
FROM items
ORDER BY cosine_distance(embedding, '[3,1,2]')
LIMIT 5;

半径过滤

通过距离阈值过滤向量,支持 L2、内积、余弦三种距离的圆域条件,可直接在 WHERE 子句中使用距离操作符。

-- L2 距离过滤:距离 < 3.0
SELECT *, ROUND((emb <-> '[1,1,1,2]')::numeric, 3)
FROM t
WHERE emb <-> '[1,1,1,2]'::vector < 3.0
ORDER BY emb <-> '[1,1,1,2]' LIMIT 10;
 
-- 内积过滤:内积 > 30(<#> 返回负内积,所以用 < -30)
SELECT *, emb <#> '[1,1,1,2]' AS dist
FROM t
WHERE emb <#> '[1,1,1,2]'::vector < -30
ORDER BY dist LIMIT 10;
 
-- 多条件叠加(标量条件 + 距离阈值)
SELECT *, ROUND((emb <-> '[1,1,1,2]')::numeric, 3)
FROM t
WHERE a < 8 AND b > 1
  AND emb <-> '[1,1,1,2]'::vector < 7.0
  AND emb <-> '[1,1,1,2]'::vector < 3.0
ORDER BY emb <-> '[1,1,1,2]' LIMIT 10;

推荐查询(Recommend Query)

通过正例和负例向量定义推荐意图,无需手动构造查询向量。支持三种融合策略。

策略原理
avg_dist正例均值 vs 负例均值的距离(默认)
best_dist最近正例距离与最远负例距离的综合
sum_dist所有正例距离之和 - 所有负例距离之和

基本用法

-- avg_dist 策略(默认):查询向量与正/负例均值的距离
SELECT id, ROUND((emb <#>
  '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]]}'
  ::recommend_query)::numeric, 3)
FROM t
ORDER BY emb <#> '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]]}'::recommend_query
LIMIT 10;
 
-- 等价写法(省略 strategy 字段,默认为 avg_dist)
SELECT id, ROUND((emb <=>
  '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]]}'
  ::recommend_query)::numeric, 3)
FROM t
ORDER BY emb <=> '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]]}'::recommend_query
LIMIT 10;

best_dist 策略

取查询向量到最近正例的距离与到最远负例的距离综合评分。

SELECT id, ROUND((emb <#>
  '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]], "strategy":"best_dist"}'
  ::recommend_query)::numeric, 3)
FROM t
ORDER BY emb <#> '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]], "strategy":"best_dist"}'::recommend_query
LIMIT 10;

sum_dist 策略

计算查询向量到所有正例距离之和与到所有负例距离之和的差值。

SELECT id, ROUND((emb <#>
  '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]], "strategy":"sum_dist"}'
  ::recommend_query)::numeric, 3)
FROM t
ORDER BY emb <#> '{"positives":[[2,3,4,5],[3,4,5,6]], "negatives":[[5,6,7,8],[6,7,8,9]], "strategy":"sum_dist"}'::recommend_query
LIMIT 10;

与标量函数配合

-- inner_product 函数 + recommend_query
SELECT inner_product(
  '[1,2,3,4]'::vector,
  '{"positives":[[1,2,3,4],[2,3,4,5]], "negatives":[[5,6,7,8],[7,8,9,10]], "strategy":"best_dist"}'
  ::recommend_query
);
 
-- cosine_distance 函数 + recommend_query
SELECT cosine_distance(
  '[1,2,3,4]'::vector,
  '{"positives":[[1,2,3,4],[2,3,4,5]], "negatives":[[5,6,7,8],[7,8,9,10]]}'
  ::recommend_query
);
 
-- l2_distance 函数 + recommend_query
SELECT l2_distance(
  '[1,2,3,4]'::vector,
  '{"positives":[[1,2,3,4],[2,3,4,5]], "negatives":[[5,6,7,8],[7,8,9,10]], "strategy":"best_dist"}'
  ::recommend_query
);

配合 split_hnsw 索引 + 量化器

CREATE TABLE t (id int, emb vector(4)) USING columnar;
INSERT INTO t VALUES (1, '[1,1,1,2]'), (2, '[2,2,2,3]'), (3, '[3,3,3,4]'), (4, '[4,4,4,5]');
 
CREATE INDEX ON t USING split_hnsw(emb vector_l2_ops) WITH (quantizer='int8');
CREATE INDEX ON t USING split_hnsw(emb vector_cosine_ops) WITH (quantizer='int8');
CREATE INDEX ON t USING split_hnsw(emb vector_ip_ops) WITH (quantizer='int8');
 
-- 带 quantizer 的推荐查询
SELECT id, ROUND(l2_distance(emb,
  '{"positives":[[2,3,4,5],[1,2,3,4]], "negatives":[[1,2,3,4],[1,2,4,3]], "strategy":"best_dist"}'
  ::recommend_query)::numeric, 3)
FROM t
ORDER BY emb <-> '{"positives":[[2,3,4,5],[1,2,3,4]], "negatives":[[1,2,3,4],[1,2,4,3]], "strategy":"best_dist"}'::recommend_query
LIMIT 10;

混合检索(多路召回融合)

对同一行数据的多个向量列分别召回,通过 FULL OUTER JOIN 合并后用融合函数重新排序,适用于语义检索召回质量优化等场景。

表结构和索引

CREATE TABLE t (id int, emb1 vector(4), emb2 vector(4)) USING columnar;
-- emb1 用 L2,emb2 用余弦
CREATE INDEX ON t USING split_hnsw(emb1 vector_l2_ops);
CREATE INDEX ON t USING split_hnsw(emb2 vector_cosine_ops);

加权 RRF 融合

WITH
recall1 AS (
    SELECT id, emb1 <-> '[0.44, 0.554, 0.34, 0.62]' AS dist1
    FROM t ORDER BY dist1 LIMIT 4
),
recall2 AS (
    SELECT id, emb2 <=> '[0.44, 0.554, 0.34, 0.62]' AS dist2
    FROM t ORDER BY dist2 LIMIT 4
),
rank1 AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY dist1) AS rank1
    FROM recall1
),
rank2 AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY dist2) AS rank2
    FROM recall2
)
SELECT
    COALESCE(rank1.id, rank2.id) AS id,
    ROUND(rank1.dist1::numeric, 3),
    ROUND(rank2.dist2::numeric, 3),
    ROUND((0.7 * rank_score(rank1.rank1) + 0.3 * rank_score(rank2.rank2))::numeric, 3) AS score
FROM rank1 FULL OUTER JOIN rank2 ON rank1.id = rank2.id
ORDER BY score DESC LIMIT 8;

距离归一化融合

WITH
recall1 AS (
    SELECT id, emb1 <-> '[...]' AS dist1 FROM t ORDER BY dist1 LIMIT 8
),
recall2 AS (
    SELECT id, emb2 <=> '[...]' AS dist2 FROM t ORDER BY dist2 LIMIT 8
),
rank1 AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY dist1) AS rank1,
           MIN(dist1) OVER () AS min1, MAX(dist1) OVER () AS max1
    FROM recall1
),
rank2 AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY dist2) AS rank2,
           MIN(dist2) OVER () AS min2, MAX(dist2) OVER () AS max2
    FROM recall2
)
SELECT
    COALESCE(rank1.id, rank2.id) AS id,
    ROUND(rank1.dist1::numeric, 3),
    ROUND(rank2.dist2::numeric, 3),
    ROUND((
      norm_dist_score(rank1.dist1, rank1.min1, rank1.max1) +
      norm_dist_score(rank2.dist2, rank2.min2, rank2.max2)
    )::numeric, 3) AS score
FROM rank1 FULL OUTER JOIN rank2 ON rank1.id = rank2.id
ORDER BY score DESC LIMIT 8;

融合辅助函数

函数公式说明
rank_score(r)1 / (r + 60)RRF 分数,r 为排名
norm_score(v, min, max)(v - min) / (max - min)正向归一化
norm_dist_score(d, min, max)(max - d) / (max - min)距离反向归一化

PQ 量化器

Product Quantization(乘积量化)将向量空间分解为多个子空间分别量化,大幅压缩索引内存占用(可压缩 10-30x),适合大规模数据集。

基本用法

CREATE TABLE t (a int PRIMARY KEY, tag text, embedding vector(4)) USING columnar;
 
INSERT INTO t VALUES
  (1, 'a', '[1.1,1.1,1.1,1.1]'),
  (2, 'a', '[1.2,1.2,1.2,1.2]'),
  (3, 'b', '[1.3,1.3,1.3,1.3]'),
  (4, 'b', '[1.4,1.4,1.4,1.4]'),
  (5, 'b', '[1.5,1.5,1.5,1.5]'),
  (6, 'c', '[1.6,1.6,1.6,1.6]'),
  (7, 'd', '[1.7,1.7,1.7,1.7]'),
  (8, 'd', '[1.8,1.8,1.8,1.8]');
 
-- PQ 量化:pq_num_segments 必须整除向量维度
CREATE INDEX ON t USING split_hnsw(embedding vector_l2_ops)
  WITH (quantizer='pq', pq_num_segments=4);
 
-- 查询照常使用
SELECT * FROM t ORDER BY embedding <-> '[2,2,2,2]' LIMIT 10;

pq_num_segments 校验

-- 4 维向量,7 不整除 4 → 报错
CREATE INDEX ON t USING split_hnsw(embedding vector_l2_ops)
  WITH (quantizer='pq', pq_num_segments=7);
-- ERROR: invalid value for parameter "pq_num_segments": "7"
 
-- 4 维向量,segments 可取 1, 2, 4
CREATE INDEX ON t USING split_hnsw(embedding vector_l2_ops)
  WITH (quantizer='pq', pq_num_segments=4);  -- OK

与其他量化器对比

-- fp16:半精度浮点,内存减半,精度损失最小
CREATE INDEX ON t USING split_hnsw(emb vector_l2_ops) WITH (quantizer='fp16');
 
-- int8:8-bit 标量量化,需 k-means 全局训练
CREATE INDEX ON t USING split_hnsw(emb vector_l2_ops) WITH (quantizer='int8');
 
-- PQ:乘积量化,压缩率最高,适合高维向量
CREATE INDEX ON t USING split_hnsw(emb vector_l2_ops)
  WITH (quantizer='pq', pq_num_segments=4);

向量数组(Vector Array)

vector(N)[] 类型支持每行存储多个向量,并提供专用的数组级距离操作符和索引。

建表和插入

CREATE TABLE t (id int PRIMARY KEY, emb vector(4)[]) USING columnar;
 
INSERT INTO t VALUES
  (1, ARRAY['[1, 1, 1, 1]'::vector, '[2, 12, 22, 32]'::vector]),
  (2, ARRAY['[3, 3, 3, 3]'::vector, '[4, 14, 24, 34]'::vector]),
  (3, ARRAY['[5, 5, 5, 5]'::vector, '[6, 16, 26, 36]'::vector]),
  (4, ARRAY['[7, 7, 7, 7]'::vector, '[8, 18, 28, 38]'::vector]),
  (5, ARRAY['[9, 9, 9, 9]'::vector, '[10, 10, 20, 30]'::vector]);

创建索引

-- 支持三种操作类(不支持 int8 量化器)
CREATE INDEX ON t USING split_hnsw(emb vector_array_l2_ops);
CREATE INDEX ON t USING split_hnsw(emb vector_array_ip_ops);
CREATE INDEX ON t USING split_hnsw(emb vector_array_cosine_ops);

数组距离操作符

-- array_l2_distance:数组间 L2 距离
SELECT id, ROUND((emb <->> ARRAY['[1,1,1,1]','[2,2,2,2]'])::numeric, 3)
FROM t ORDER BY emb <->> ARRAY['[1,1,1,1]','[2,2,2,2]'] LIMIT 5;
 
-- array_cosine_distance:数组间余弦距离
SELECT id, ROUND((emb <=>> ARRAY['[1,1,1,1]','[2,2,2,2]'])::numeric, 3)
FROM t ORDER BY emb <=>> ARRAY['[1,1,1,1]','[2,2,2,2]'] LIMIT 5;
 
-- array_inner_product:数组间内积(返回负值)
SELECT id, ROUND((emb <#>> ARRAY['[1,1,1,1]','[2,2,2,2]'])::numeric, 3)
FROM t ORDER BY emb <#>> ARRAY['[1,1,1,1]','[2,2,2,2]'] LIMIT 5;

标量函数查询

-- 自定义数组顺序计算距离(不使用操作符)
SELECT id, ROUND(array_l2_distance(
  ARRAY['[1,2,3,5]','[2,3,4,6]'], emb)::numeric, 3) AS dist
FROM t ORDER BY ARRAY['[1,2,3,5]','[2,3,4,6]'] <->> emb LIMIT 10;
 
SELECT id, ROUND(array_cosine_distance(
  ARRAY['[1,2,3,4]','[2,3,4,5]'], emb)::numeric, 3) AS dist
FROM t ORDER BY ARRAY['[1,2,3,4]','[2,3,4,5]'] <=>> emb LIMIT 10;
 
SELECT id, ROUND(array_inner_product(
  ARRAY['[1,1,1,1]','[2,2,2,2]'], emb)::numeric, 3) AS dist
FROM t ORDER BY ARRAY['[1,1,1,1]','[2,2,2,2]'] <#>> emb LIMIT 10;

距离阈值过滤 + 操作符可交换

-- emb <#>> literal 形式
SELECT id, emb <#>> ARRAY['[1,1,1,1]','[2,2,2,2]'] AS dist
FROM t
WHERE emb <#>> ARRAY['[1,1,1,1]','[2,2,2,2]'] < -210
ORDER BY emb <#>> ARRAY['[1,1,1,1]','[2,2,2,2]'] LIMIT 10;
 
-- literal <#>> emb 形式(等价,操作符可交换)
SELECT id, ARRAY['[1,1,1,1]','[2,2,2,2]'] <#>> emb AS dist
FROM t
WHERE ARRAY['[1,1,1,1]','[2,2,2,2]'] <#>> emb < -210
ORDER BY ARRAY['[1,1,1,1]','[2,2,2,2]'] <#>> emb LIMIT 10;

操作符速查

操作符函数索引操作类
<->>array_l2_distancevector_array_l2_ops
<=>>array_cosine_distancevector_array_cosine_ops
<#>>array_inner_productvector_array_ip_ops

注意:当使用量化索引时,必须使用对应的函数进行查询,不能使用 <-><#><=> 操作符。