索引内存消耗问题
索引的选择对于向量召回的性能至关重要,Milvus 支持了 Annoy,Faiss,HNSW等多种不同的索引,大部分索引必须加载进内存. 用户可以根据对延迟、内存使用和召回率的需求进行选择。对于大数据量,内存不足的场景,提供磁盘索引解决方案:
-
DiskANN
-
DiskANN 依赖高性能的磁盘索引,借助 NVMe 磁盘缓存全量数据,在内存中只存储了量化后的数据。
-
DiskANN 适用于对于查询 Recall 要求较高,QPS 不高的场景。
-
DiskANN 示意图
-
DiskANN 的关键参数:
-
search_list: search_list 越大,recall 越高而性能越差。search_list 的大小不应该小于 K。而对于较小的 K,推荐把 search_list 和 K 的比值设置得相对大一些, 这个比值随着 K 增大可以逐渐靠近
-
IVF_PQ
-
对于精确度要求不高的场景或者性能要求极高的场景。
-
IVF PQ 的核心是两个算法,IVF + PQ 量化,其中量化可以大幅减少向量的占用内存量。
-
-
-
IVF 参数
-
nlist:一般建议 nlist = 4*sqrt(N),对于 Milvus 而言,一个 Segment 默认是 512M 数据,对于 128dim 向量而言,一个 segment 包含 100w 数据,因此最佳 nlist 在 1000 左右。
-
nprobe:nprobe 可以 Search 时调整搜索的数据量,nprobe 越大,recall 越高,但性能越差。具体的 nprobe 需要根据查询的精度要求决定,从 nprobe = 16 开始会是一个不错的尝试。
-
-
PQ 参数
-
M: 向量做 PQ 的分段数目,一般建议设置为向量维数的 1/4,M 取值越小内存占用越小,查询速度越快,精度也变得更加低。
-
Nbits: 每段量化器占用的 bit 数目,默认为 8,不建议调整。
-
大数据量性能瓶颈问题
解决方案:
分布式云原生部署方案, 数据分片分表分区,合理的预计数据量,表数目大小,QPS 参数等指标
在部署 Milvus 之前,首先需要决定机器的资源、规格、以及一些依赖的资源,以下是你需要考虑的因素:
-
有多少张表?
-
每张表的数据量有多少?
-
每张表的 QPS 需求有多少?
-
是否需要存标量字段,如果有字符串,字符串的平均长度是多少?
-
是否有删除和流式插入,每天大概有多少比例的数据需要被更新?
基于以上因素,可以遵循以下经验结论:
- 节点资源占用可以通过 sizing tool[2] 进行计算,通常情况下 8G 内存可以支持超过 5MB 的 128dim 向量数据和 1MB 的 768dim 数据。
- 默认情况下,Milvus 会创建 256 个消息队列 topic。如果表数目比较少,可以调整 rootCoord.dmlChannelNum 减少 topic 数目降低消息队列负载。
- 默认情况下,每个 collection 会使用 2 个消息队列 topic(shard),如果写入非常大或者数据量极大,需要调整 collection 的 shard 数目。建议每个 shard 写入 / 删除不超过 10M/s,单个 shard 的数据量不大于 1B 向量,shard 数目过大也会影响写入性能,因此不建议单表超过 8 个 shard。
- 根据 benchmark[3] 结果计算需要的 CPU 资源。对于小数据量场景(小于 5mn),使用多副本可以扩展查询性能,但建议副本数目不要超过 10 个。对于中大数据量场景,通常扩容 querynode 就可以自动负载均衡,不需要使用多副本提升 QPS.
- 所有的标量字段目前也会加载进内存中,也会消耗内存,请在容量规划时预留原始数据类型两倍以上的内存。
- Milvus 在存储数据的过程中,存在较多冗余数据(https://github.com/milvus-io/milvus/issues/20453)。考虑到 Minio 的 2,4 纠删码存在两副本冗余,我们建议 Minio 至少包含 6 倍以上的数据的磁盘存储。同时 Pulsar/Kafka 需要包含近五天写入量三倍的存储。合理调整数据的保留时间和 GC 时间可以很大程度上减少磁盘的使用,默认情况下数据会被保留 5 天。个人建议适当缩短数据过期时间,但尽可能保留 1 天以上避免数据丢失或误删除。
- Etcd 作为 Milvus 的元信息存储和服务发现节点,请尽可能使用 ssd 磁盘并独立部署。通常 Etcd 的内存使用不会超过 4GB,通过调整参数可以较快地清理 etcd 中的历史版本减少内存使用。
- Pulsar/Kafka 作为 Milvus 的日志存储,其依赖的 zookeeper 集群对性能要求也比较高,建议使用 SSD 并独立部署。
标量过滤性能如何保证
作为数据库,Milvus 支持了删除、标量过滤、TimeTravel 等高级特性。如果不了解底层原理,使用这些高级功能可能会对稳定性和性能造成比较严重的影响,以下是一些使用注意事项:
-
Milvus 使用的是
前过滤
,即先做标量过滤生成 Bitset,在向量检索的过程中基于 Bitset 去除掉不满足条件的 entity。对于 HNSW 这一类的图索引而言,标量过滤并不会加速查询,反而可能导致性能变差。特别是对于过滤性很强的条件(比如 PK=1 这种全局唯一的条件),标量过滤甚至会导致单次查询的时间长于爆搜。针对这种情况,用户也可以选择通过后过滤的方式绕过,先基于 Milvus 查出 TopK 的数据,再基于其他数据库进行过滤。 -
对于过滤条件相对比较确定的场景,使用 Partition 把数据进行物理分区,在查询的时候指定 Partition 性能更好。
-
Milvus 的删除是标记删除,在 compaction 时会清理,因此删除的数据依然会占据内存。大量删除也会造成查询性能下降,同时大量 compaction 可能造成建索引压力变大等一系列影响。在需要大量频繁删除的场景,可能需要进行一些 compaction 参数的调整,保证删除的数据能够被及时清理。
-
Milvus 支持了数据自动过期功能(TTL),可以定时清理过期数据。
-
如果需要全量更新一个 Collection 的数据,推荐使用新建表 + 导入数据 + Alias 切换的方案。
-
制定 Output field 时,如果要获取标量字段,会从对象存储上获取,吞吐和延迟都会受到较大影响。
当然,Milvus 后续的版本会对以上能力做针对性的优化,尤其是删除和标量过滤的场景。
外部解决方案包括:
外挂CK、ES等分析引擎作为标量过滤组件