IVFFlat 索引
VexDB 支持在向量数据上创建 IVFFlat 索引,采用 IVF 算法实现快速查询向量数据的目的。
原理介绍
IVF算法
IVF(Inverted File System,倒排系统)算法的主要原理在于将所有向量划分成多个簇,查询时只搜索最相邻的簇,减少计算量,从而加速近似最近邻查询。
首先要对数据进行聚类。聚类是指按照某个特定标准(比如距离)把相似的数据划分到一起,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性也尽可能地大。在这个步骤中,按照 k-means 聚类算法将数据集分成多个簇,每个簇都代表一个具有类似特征或语义接近的向量集群,这种划分为检索过程的有效开展提供了便利。每个簇都有一个中心点,这个点的坐标正是倒排索引中的键。
IVFFlat 中索引的使用将搜索限制在与最近中心点相关的区域,减少了需要检查的向量数量,从而加速了搜索过程。
然而,明确了一个最近中心点表示的搜索范围后,可能存在其他簇中的向量比此范围中的向量更接近检索条件的情况(如下图所示),这也是搜索存在误差的主要原因。可以通过后文介绍的查询参数决定搜索过程要考虑的中心点的数量(簇的个数),通过适当扩大搜索范围来减小误差。

*上图中,距离查询向量最近的向量并非处于最近中心点所代表的绿色区域中,而是与黄色区域中的一个向量距离最近。
索引参数
索引构建参数
参数名称 | 取值说明 | 参数描述 |
---|---|---|
ivf_nlist | 取值范围:1~65535 默认值:100 | 索引中倒排列表的数量,即划分的“簇”的个数。 |
enable_toast | 取值范围:true/false 默认值:true | 向量索引 tuple 是否启用 toast 压缩。 |
parallel_workers | 取值范围:0~64 默认值:0 | 并行构建参数,构建索引并行计算线程数。 |
GUC 构建参数
参数名称 | 取值说明 | 参数描述 |
---|---|---|
ivf_extend_file_block_batch_count | 取值范围:1~1024 默认值:64 | 该实例级参数指定了索引文件写满后,扩展时批量分配的 8K 数据页的个数。通过批量分配 ivf_extend_file_block_batch_count 个页,可以在读写时更高效地利用操作系统缓存。 |
GUC 查询参数
参数名称 | 取值说明 | 参数描述 |
---|---|---|
ivf_probes | 取值范围:1~65535 默认值:1 | 会话级参数,指定查询范围包含的倒排列表数,即查询的“簇”的个数。ivf_probes 的上限为ivf_nlist,如果设置为超过 ivf_nlist 的值, 系统会自动设置 ivf_probes = ivf_nlist。 |
max_vector_indexer_query_threads | 取值范围:整型,0,256 默认值:0,表示关闭向量并行检索。 | 同时进行向量检索的最大并行线程数。 |
索引构建操作符
索引操作符 | 操作符描述 |
---|---|
floatvector_cosine_ops | 计算向量的余弦距离。 |
floatvector_l2_ops | 计算向量的欧几里得距离。 |
floatvector_ip_ops | 计算向量的内积。 |
说明
上述参数在索引构建阶段和向量查询阶段的具体使用方式,可参考使用示例。
使用建议
- 建立 IVFFlat 索引时,表中数据不宜过少,建议不少于 104 条数据。
- 建议将所有需要查询的数据提前插入表中,最后再建立索引;如果决定先建立索引再导入数据,在数据导入完成后需要重建索引。
- 建立索引后如果数据变化(含插入、删除、更新)超过 20% 会产生数据分布漂移,导致查询精度下降与查询时间不稳定,建议每更新 40%~60% 数据重建一次索引。
- 当需要提升索引构建速度,可以适当增大 parallel_workers , 建议设置为机器 CPU 核数的 75%,最大值 64。
- 通常建议 ivf_nlist 的值至少为 10。较高的 ivf_nlist 值可通过缩小查询的搜索空间来加快查询速度。然而,缩小查询搜索空间可能会导致召回率降低。
- 对于少于一百万行的数据集,建议设置 ivf_lists = rows / 1000;对于超过一百万行的数据集,建议设置 ivf_lists= sqrt(rows)。
- 当需要提升召回率时,可适当增大 ivf_probes,ivf_probes 越大,召回率越高,查询越慢,当 ivf_probes = ivf_nlist 时,索引查询等同于全表扫描。
- 如果向量检索时,查询未按照预期走索引,请参考向量查询不走索引中介绍的内容进行排查,确认查询是否满足走索引的条件。
使用示例
- 数据准备:创建生成随机向量的函数。
CREATE OR REPLACE FUNCTION random_array(dim integer,min_value int, max_value int) RETURNS text AS $$ SELECT REGEXP_REPLACE(REGEXP_REPLACE(array_agg(round(random()* (max_value - min_value + 1) + min_value,3))::text,'{','['),'}',']') FROM generate_series(1, dim); $$ LANGUAGE SQL VOLATILE COST 1;
- 创建含向量字段的测试表,并调用上一步创建的函数插入向量数据。
drop table t_1194690; CREATE TABLE t_1194690(id BIGINT, v floatVECTOR(15)); INSERT INTO t_1194690 SELECT i, random_array(15,1,3)::floatvector(15) FROM generate_series(1, 10000) AS i;
- 创建IVFFlat 索引。
- 按余弦距离构建索引:
CREATE INDEX idx_1194690a ON t_1194690 USING ivfflat(v floatvector_cosine_ops)WITH(ivf_nlist= 100,enable_toast=true, parallel_workers=1);
- 按欧几里得距离构建索引:
CREATE INDEX idx_1194690b ON t_1194690 USING ivfflat(v floatvector_l2_ops)WITH(ivf_nlist= 100,enable_toast=true, parallel_workers=1);
- 按内积构建索引:
CREATE INDEX idx_1194690c ON
- 按余弦距离构建索引:
- 进行向量相似性搜索。
设置查询参数:set ivf_probes = 10;
- 按余弦距离排序:
SELECT * FROM t_1194690 ORDER BY v <=> '[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]'::floatvector LIMIT 10;
- 按欧几里得距离排序:
SELECT * FROM t_1194690 ORDER BY v <-> '[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]'::floatvector LIMIT 10;
- 按内积排序:
SELECT * FROM t_1194690 ORDER BY v <#> '[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]'::floatvector LIMIT 10;
- 按余弦距离排序: