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 时,索引查询等同于全表扫描。
  • 如果向量检索时,查询未按照预期走索引,请参考向量查询不走索引中介绍的内容进行排查,确认查询是否满足走索引的条件。

使用示例

  1. 数据准备:创建生成随机向量的函数。
    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;
    
  2. 创建含向量字段的测试表,并调用上一步创建的函数插入向量数据。
    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;
    
  3. 创建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
      
  4. 进行向量相似性搜索。
    设置查询参数:
    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;
      

需要帮助?

扫码添加企业微信
获得专业技术支持

企业微信二维码
🎯 快速响应💡 专业解答