得物技术的 Milvus 探究与压测分析

#01

背景

得物技术最近用到了向量搜索,所以要对 Milvus 进行压测,同时为了更加深入分析压测中遇到的问题,也对 Milvus 的部分源码与文档进行了走读,其中遇到了一些问题与疑惑,我们也直接与 Milvus 社区或开源贡献者沟通。

通过压测,我们发现某场景下存在 Milvus 的性能提升不上去的问题,并给出基于该场景的解决方案,社区反馈给 Milvus 官方。

以下为 Milvus 的设计与压测中遇到的一些问题与解决或跟进方案。

#02

向量搜索与 Milvus

2.1 向量搜索

向量搜索简称 ANNS,英文全称 “Approximate Nearest Neighbor Search”,大致概念是从一堆向量中找出与某个目标向量距离最近的 N 个向量。最简单粗暴的搜索方式是暴力搜索,但是可以通过扩增索引的方式加快搜索速度,提升大规模查询的 QPS。

当前的向量索引可以分为四大类:基于树的索引、基于图的索引、基于哈希的索引、基于量化的索引。其中图思路由于较高的召回率、较好的性能和较多的后期优化思路,脱颖而出。

2.2 Milvus

Milvus(主要针对 2.0 以上版本)是一款云原生向量数据库,支持向量的插入,ANNS 搜索等。Milvus 能够很好地应对海量向量数据,它集成了目前在向量相似性计算领域比较知名的几个开源库(Faiss,SPTAG 等),通过对数据和硬件算力的合理调度,以获得最优的搜索性能。

#03

Milvus 架构介绍

3.1 Milvus 的数据集概念

数据集概念

  • collection:数据集,类似于 mysql 的表。

  • channel:基于主键把数据集细分为很多 channel,在数据写入与查询时,对应与 msg borker 中的通道的概念。

  • partion:partion 把数据集进行了一层划分,常见的 partion 比如日期,按照日期分类存储数据,partion 与 channel 是正交的关系。

  • segment:Milvus 数据集的最小单位,具体的索引都是基于 segment 去构建的,查询的最小单位也是 segment。

官网给出数据集读取写入例子:https://milvus.io/docs/v2.1.x/example_code.md

数据集查询工具:https://github.com/milvus-io/birdwatcher

以下利用 birdwatcher 展示 collection 与 segment 信息:以上输出结果,对该工具进行了改造。

具体一个 segment 在 etcd 中所有的信息:

3.2 Milvus 架构图

Milvus 官网给出的架构图如下:按照分组又可以分为以下几大类别:简单介绍下以上各个微服务的功能:

  • 系统门面:

    • Proxy:所有 SDK 查询都会经过 proxy,proxy 会把写数据投递到 message borker 中对应不同的 channel,proxy 会处理三类数据:写请求,读请求,控制类请求。
  • 系统协调者:

    • RootCoord:类似于传统 master 角色主要做一些 DDL、DCL 的管理,比如:创建 Collection,删除 Collection,或对于 partion 做管理。此外还有一个更大的责任,rootCoord 给系统分配全局唯一时间戳。
  • 写数据:

    • DataCoord:协调者,分配,管理 segment,管理 dataNode,处理 dataNode 的故障恢复等。
    • DataNode:消费来自数据流的数据,进行数据序列化,负责把 log 数据转成 log snapshot,刷新到磁盘中。
  • 索引创建:

    • IndexCoord:对 sealed SegMent 创建索引,管理 indexNode。
    • IndexNode:负责具体的索引创建事宜。
  • 查询:

    • QueryCoord:数据查询管理,负责管理 QueryNode。
    • QueryNode:负责具体的数据查询事宜。
  • 元信息与元数据存储:

    • MetaStore:metastore 使用 ETCD 存储,主要负责元信息与元数据的存储。比如:表结构,Segment 结构,全局时间戳等。
  • 写数据消息投递:

    • Log Borker:Log broker 采用了 pulsar。最新的 2.0 及以上版本中,写入的数据都是先写入 Log Broker,然后 DataNode 从 Log Broker 中读取。
  • 数据与索引存储:

    • Object Store:Object store 当前采用了 minio,主要用来存储数据,索引等。

以上可以看出微服务比较多,微服务之间的通信方式主要有以下几种:

#04

Milvus 向量写入与读取链路

4.1 Milvus 向量写入路径

  • Proxy 通过 produce 把数据写入到 Message borker 的物理 channel 中。
  • DataNode 作为消费者消费数据。
  • DataNode 定期把消费数据存到 Object store 中。
  • DataNode 会定期通知 dataCoord 记录数据元信息。

4.2 Milvus 向量搜索路径

以下当前最新版本 2.1.4 的读流程,与网上的读流程版本链路不同,应该是做了改造。

  • Proxy 收到向量搜索(ANNS)请求后,会把请求丢给 shard leader query node。

  • Leader querynode 会依据每个 segments 的分布,把 ANNS 请求分发给每个 Query Node。Query Node 内部会基于最小搜索单位 Segment,cpu 核数等去做并行查询,并做结果 reduce。

  • Proxy 收到所有的请求后,会对 search 结果做 reduce,并返回给客户端。

#05

Milvus 压测中的问题分析

压测版本:Milvus-2.1.4

数据维度:512 dim

索引:

5.1 压测结果

向量个数 索引 规格 QPS 99%耗时
十万*512dim FLAT 2*(8cpu*16Gi) 880 82ms
十万*512dim FLAT 2*(16cpu*16Gi) 1489 62ms
百万*512dim FLAT 2*(16cpu*16Gi) 240 200ms
千万*512dim FLAT 2*(16CPU*32Gi) 20 1.98s

5.2  压测中遇到的问题与分析

  • QPS 与 CPU 使用率压不上去,CPU 很难超过 50%(已经优化)。

    • 现象描述:压测过程中,发现 QPS 始终压不上去,仔细排查发现查询节点的 cpu 使用率上不去,导致 QPS 也上不去。

    • 解决方案:初步怀疑是查询节点调度问题,经过各种排查,发现与一个调度参数 scheduler.cpuRation 高度相关。以下是该参数在不同值的 QPS 情况。

规格 scheduler.cpuRation QPS
2*(8cpu*16Gi) 20 385
2*(8cpu*16Gi) 100 768
2*(8cpu*16Gi) 120 913
2*(8cpu*16Gi) 140 880

该参数主要用来评估一个 search task 的 cpu 使用情况,该参数越高,预示该 task 使用 cpu 越多,调度的时候,多个 task 去查询的并行数量就会少一些。现在怀疑并行 task 太多,并不会达到很高的 QPS。

Milvus 并没有公开该参数配置,已经通过 issue/enhancement 提给 Milvus 社区,后续版本应该会有所优化。

  • 扩容查询节点后,短时间内 segments 没有自动均衡(怀疑,跟进中)。

    • 现象描述:比如当前线上有两个查询节点,50 个 segments 均分在两个 nodes 上。压测中多次发现如果增加一个 node 后,segments 并不会自动均衡到新的 node 上。

    • 当前进度:整个压测过程中做了三次写入,有两次没有自动均衡,最后一次自动均衡了。跟 Milvus 社区维护人员咨询过该问题,他们认为理论上扩增是会自动均衡的。这与我们测出的结果不匹配,后续会继续跟进,找到问题所在。

  • 持续大规模写了很久后,会导致大量 growing segment,导致查询性能下降(跟进中)。

    • 现象描述:多个线程,持续大规模插入向量数据后,通过日志排查,发现部分部分查询节点上的 segment 一直处于 growing 状态,虽然这些 segment 在写入节点已经 sealed 了,但是某个查询节点并不会自动重新加载这些 sealed segments,而是一直认为这些节点处于 growing 状态。由于 growing 状态的 segment 查询时不用索引,而是暴力搜索,这样会导致查询变得比较慢,需要手动操作 release。

    • 当前进度:跟 Milvus 社区维护人员咨询过该问题,后续还要持续跟进,找出原因并改进。

  • 版本升级后,原有数据不兼容(已有方案)。

    • 现象描述:Milvus 版本由 2.1.4 升级到最新版后,原有数据没办法加载,且启动不了。回退版本后,发现数据元信息已经被写坏了,没法加载。
    • 解决方案:后续稳定后,谨慎做版本升级,或升级前做好充分调研。另外官方给出的建议是升级前先 merge 数据。
  • 千万级别数据,压测 QPS 不能达到预期(跟进中)。

    • 现象描述:当数据插入千万级别后,发现压测提升 QPS 比较难,99% 耗时下降也比较快,即使通过提升 cpu 核的个数,提升也不是很明显。
    比如以下是使用两个 32 核 16G:

    • 解决方案:这个可能跟我们使用 FLAT 索引有关,后续会尝试新的索引方式压测。
  • 不要通过 deployment 扩容缩容,尽量通过 helm 去操作。

    • 现象描述:当通过 deployment 扩容后,因为参数不能统一修改的问题,做不到平滑扩容,比如扩容后可能需要重新 release 与 load 数据,造成短时间中断。

所以官网也给出建议尽量通过 helm 去平滑扩容。

#06

总结

经过压测,Milvus 是可以满足我们当前业务场景的。以上压测中的一些遗留问题,我们还在跟进中,比如:大量 growing segment 问题,节点扩增等问题。这些问题并不是 100% 出现的,有些是在我们极端测试条件下才出现的,后续我们还会持续测试,定位原因,并反馈给社区进一步优化。以上压测的索引采用的是 FLAT,官方建议我们采用图索引可以取得更高性能。由于我们当前的业务场景要用到 FLAT 索引,所以当前先基于 FLAT 索引去压测,后续会用到图索引,也会进行压测。

通过对 Milvus 的压测,顺便了解并学习下 Milvus 的设计。总体来说 Milvus 是一款优秀的云原生向量数据库,它的一些设计理念还是比较先进的,把向量搜索与 k8s 结合在一起,通过简单的查询节点扩增便可以线性提升向量搜索的性能。对于一款分布式数据库,它实现了读写分离,存算分离,官网给出的文档也比较丰富,工具也比较多,比如:attu,birdwatcher 等。


Zilliz 是向量数据库系统领域的开拓者和全球领先者,研发面向 AI 生产系统的向量数据库系统。Zilliz 以发掘非结构化数据价值为使命,致力于打造面向 AI 应用的新一代数据库技术,帮助企业便捷地开发 AI 应用。Zilliz 的产品能显著降低管理 AI 数据基础设施的成本,帮助 AI 技术赋能更多的企业、组织和个人。

本文分享自微信公众号 - ZILLIZ(Zilliztech)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章