性能调优

插入性能调优

“数据插入”到“数据写入磁盘”的基本流程请参考 存储操作

如果数据量小于单次插入上限(256 MB),批量插入比单条插入要高效得多。

系统配置中的两个参数对插入性能有影响:

  • wal.enable

该参数用于开启或关闭 预写日志(WAL, Write Ahead Log) 功能(默认开启)。开启和关闭预写日志功能时,插入数据的流程分别如下:

  • 开启预写日志功能时,预写日志模块先将数据写入磁盘,然后返回插入操作。
  • 关闭预写日志功能时,数据插入速度更快。系统直接将数据写入内存中的可写缓冲区,并立即返回插入操作。

但是对于 删除操作 来说,打开预写日志功能时速度更快。为了保证数据的可靠性,我们建议打开预写日志。

  • storage.auto_flush_interval

该参数是指后台落盘任务的间隔时间,默认值为 1 秒。根据 Milvus 数据段合并策略,增大该值可减少段合并的次数,减少磁盘 I/O,提高插入操作的吞吐量。

Milvus 无法搜索到在该时间间隔内未落盘的数据。

另外,建立集合时的参数 segment_row_limit 也对插入性能有影响。该参数的默认值为 1024 MB,最大值为 4096 MB。该参数越大,将文件合并到该值设定的大小所需的次数就越多,影响插入操作的吞吐量。该参数越小,则产生的数据段越多,查询性能可能会变差。

除了软件层面的因素外,网络带宽和存储介质对插入操作性能也有影响。

查询性能调优

影响查询性能的因素包括硬件环境、系统参数、索引、查询规模等。

硬件环境

  • 使用 CPU 计算时,查询性能取决于 CPU 的主频、核心数和支持的指令集。

Milvus 在支持 AVX 指令集的 CPU 上的查询性能较好。

  • 使用 GPU 计算时,查询性能取决于 GPU 的并行计算能力以及传输带宽。

系统参数

系统参数配置请参考 Milvus 服务端配置

  • cache.cache_size

该参数是指用于驻留查询数据的缓存空间大小,默认值为 4 GB。如果该缓存空间不足以容纳所需的数据,查询时会从磁盘临时加载数据,严重影响查询性能。因此,cache_size 应当大于查询所需的数据量。

  • 浮点型原始向量的数据量可根据“向量总条数 × 维度 × 4”来估算。
  • 二进制型原始向量的数据量可根据“向量总条数 × 维度 ÷ 8”来估算。

索引建立完成后(不包括 FLAT),索引文件需要额外占用磁盘空间,查询只需加载索引文件。

  • IVF_FLAT 索引的数据量和其原始向量总数据量基本相等。
  • IVF_SQ8 / IVF_SQ8H 索引的数据量相当于其原始向量总数据量的 25% ~ 30%。
  • IVF_PQ 索引的数据量根据其参数变化,一般低于其原始向量总数据量的 10%。
  • HNSW/RNSG/ANNOY 索引的数据量都大于其原始向量总数据量。

通过调用 get_collection_stats 接口,可准确获知查询一个集合所需的数据总量。

  • gpu.gpu_search_threshold

在 GPU 版本中,当目标向量数量大于等于该参数设定的值,将会启用 GPU 查询。该参数的默认值为 1000。

GPU 查询的性能取决于 CPU 将数据加载进显存的速度以及 GPU 的计算能力。在目标向量数量较少时,无法充分发挥出 GPU 并行计算的优势。只有当目标向量数量达到某个阈值后,GPU 的查询性能才会优于 CPU。在实际使用中,可根据实验对比得出该参数的理想值。

  • gpu.search_resources

指定用于查询的 GPU 设备。对于数据插入和查询并发的场景,使用 GPU 建索引可避免后台建索引任务和查询任务争抢 CPU 资源。

  • gpu.build_index_resources

指定用于建索引的 GPU 设备。对于查询目标向量数量较大的场景,使用多 GPU 可显著提高查询效率。

索引

向量索引的基本概念请参考 向量索引概述

选择合适的索引需要在存储空间、查询性能、查询召回率等多个指标中权衡。

  • FLAT 索引

FLAT 是对向量的暴力搜索(brute-force search),速度最慢,但召回率最高(100%),磁盘空间占用最小。

随着目标向量数量增多,使用 CPU 做 FLAT 查询的耗时呈线性上升关系;而使用 GPU 查询时,批量查询的效率高,目标向量数量增加对查询耗时影响不大。

  • IVF 系列索引

IVF 系列索引包括 IVF_FLAT、IVF_SQ8/IVF_SQ8H 和 IVF_PQ。IVF_SQ8/IVF_SQ8H 和 IVF_PQ 索引对向量数据做了有损压缩,磁盘占用量较少。

IVF 索引都有两个相同的参数:nlistnprobe,相关原理可参考 索引概览

根据其原理,可估算出使用 IVF 索引进行查询时的计算量。

  • 单个数据段计算量可估算为:目标向量数量 × (nlist + (段内向量数 ÷ nlist)× nprobe)
  • 数据段的数量可估算为:集合数据总量 ÷ segment_row_limit
  • 对集合查询所需的计算总量则为:单个数据段计算量 × 数据段数量

通过估算得出的计算总量越大,查询耗时越长。实际使用中可根据以上公式确定合理的参数,在满足召回率的前提下获得较高的查询性能。

在持续插入数据的场景下,由于对大小未达到 segment_row_limit 的数据段未建立索引,对其使用的查询方式是暴力搜索。计算量为:目标向量数量 x 该数据段向量总数。

  • HNSW / RNSG / ANNOY 索引

HNSW、RNSG、ANNOY 的索引参数对查询性能的影响较为复杂,建议参考 索引概览

其他

  • 结果集

结果集的规模取决于目标向量数量和 topktopk 的大小对计算的影响不大。但在目标向量数量和 topk 都较大的情况下,结果集序列化和网络传输的耗时会相应增加。

  • MySQL

Milvus 使用 MySQL 作为元数据后端服务。Milvus 在查询数据时会多次访问 MySQL 以获取元数据信息,因此 MySQL 服务的响应速度对 Milvus 的查询性能有较大影响。

  • 预加载

首次查询需要先将数据从磁盘读入缓存,因此耗时较长。为避免首次查询加载数据,可预先调用 load_collection 接口,或使用系统参数 preload_collection 指定启动 Milvus 时要预先加载的集合。

  • 段数据整理

数据段整理 中提到,Milvus 在查询数据时将 delete_docs 读入内存以过滤被删除的实体。调用 compact 接口可清理被删除的实体,减少过滤操作,从而提高查询性能。

存储优化

  • 数据段整理

数据段整理 中提到,被删除的实体不参与计算,并且占用磁盘空间。如果有大量的实体已被删除,你可以调用 compact 接口来释放磁盘空间。

常见问题

为什么查询时 GPU 一直空闲?

此时应该是在用 CPU 进行查询。如果要用 GPU 进行查询,需要在配置文件中将 gpu_search_threshold 的值设置为小于 nq (每次查询的向量条数) 。可以将 gpu_search_threshold 的值调整为期望开启 GPU 搜索的 nq 数。若 nq 小于该值,则用 CPU 查询,否则将使用 GPU 查询。不建议在查询批量较小时使用 GPU 搜索。

为什么搜索的速度非常慢? 请首先检查 milvus.yamlcache.cache_size 参数是否大于集合中的数据量。 为什么同样的数据量,用 GPU 查询比 CPU 查询慢?

一般来说,当 nq(每次查询的向量条数)较小时,用 CPU 查询比较快。只有当 nq 较大(约大于 500)时,用 GPU 查询才会更有优势。

因为在 Milvus 中,每次用 GPU 查询都需要将数据从内存加载到显存。只有当 GPU 查询节省的计算时间能抵消掉数据加载的时间,才能体现出 GPU 查询的优势。

为什么有时候小的数据集查询时间反而更长? 如果数据文件的大小小于创建集合时 segment_row_limit 参数的值,Milvus 则不会为此数据文件构建索引。因此,小的数据集有可能查询时间会更长。你还可以调用 create_index 建立索引。