为可扩展的相似性搜索建立向量数据库
封面图片
本文由栾晓帆撰写,倪安琪和余晴转译。
据统计,全球约有 80%-90% 的数据是非结构化数据。在互联网快速发展的推动下,预计未来几年非结构化数据将出现爆炸式增长。因此,企业迫切需要一个功能强大的数据库,帮助他们更好地处理和理解这类数据。然而,开发数据库总是说起来容易做起来难。本文旨在分享构建用于可扩展相似性搜索的开源云原生向量数据库 Milvus 的思考过程和设计原则。本文还详细介绍了 Milvus 的架构。
跳转到
非结构化数据需要完整的基础软件栈
随着互联网的发展和演变,非结构化数据变得越来越常见,包括电子邮件、论文、物联网传感器数据、Facebook 照片、蛋白质结构等。为了让计算机理解和处理非结构化数据,需要使用Embeddings 技术将这些数据转换成向量。
Milvus 对这些向量进行存储和索引,并通过计算两个向量的相似性距离来分析它们之间的相关性。如果两个嵌入向量非常相似,说明原始数据源也很相似。
处理非结构化数据的工作流程。
向量和标量
标量是一种只用一种测量方法(幅值)来描述的量。标量可以用数字表示。例如,一辆汽车以每小时 80 公里的速度行驶。在这里,速度(80km/h)就是一个标量。与此同时,向量是一个至少用两个量--大小和方向--来描述的量。如果一辆汽车以 80 km/h 的速度向西行驶,这里的速度(80 km/h 向西)就是一个向量。下图是常见标量和向量的示例。
标量与向量
由于大多数重要数据都有一个以上的属性,如果我们将这些数据转换成向量,就能更好地理解它们。我们处理向量数据的一个常用方法是使用欧氏距离、内积、谷本距离、汉明距离等指标计算向量之间的距离。距离越近,向量越相似。为了高效地查询海量向量数据集,我们可以通过建立向量索引来组织向量数据。在对数据集建立索引后,查询可以被路由到最有可能包含与输入查询相似的向量的数据集群或数据子集。
要了解有关索引的更多信息,请参阅向量索引。
从向量搜索引擎到向量数据库
从一开始,Milvus 2.0 的设计目的就不仅仅是作为一个搜索引擎,更重要的是作为一个功能强大的向量数据库。
有一种方法可以帮助你理解其中的区别,那就是类比InnoDB和MySQL,或者Lucene和Elasticsearch。
就像 MySQL 和 Elasticsearch 一样,Milvus 也是建立在Faiss、HNSW、Annoy 等开源库之上的,这些开源库的重点是提供搜索功能和确保搜索性能。不过,如果把 Milvus 仅仅贬低为 Faiss 上面的一层,那就有失公允了,因为它可以存储、检索、分析向量,而且与其他数据库一样,还为 CRUD 操作提供了标准接口。此外,Milvus 还拥有以下功能:
- 分片和分区
- 复制
- 灾难恢复
- 负载平衡
- 查询解析器或优化器
向量数据库
如需更全面地了解什么是向量数据库,请阅读此处的博客。
云原生方法
原生方法
从共享无到共享存储,再到共享有
传统数据库过去采用 "无共享 "架构,即分布式系统中的节点相互独立,但通过网络连接。节点之间不共享内存或存储。然而,Snowflake引入了 "共享存储 "架构,将计算(查询处理)与存储(数据库存储)分开,从而彻底改变了整个行业。通过共享存储架构,数据库可以实现更高的可用性和可扩展性,并减少数据重复。受 Snowflake 的启发,许多公司开始利用基于云的基础设施进行数据持久化,同时使用本地存储进行缓存。这种数据库架构被称为 "共享的东西"(shared something),已成为当前大多数应用的主流架构。
除了 "共享的东西 "架构外,Milvus 还使用 Kubernetes 管理其执行引擎,并用微服务分离读、写和其他服务,从而支持每个组件的灵活扩展。
数据库即服务(DBaaS)
数据库即服务是一个热门趋势,因为许多用户不仅关心常规的数据库功能,还渴望获得更多不同的服务。这意味着,除了传统的 CRUD 操作符,我们的数据库还要丰富服务类型,如数据库管理、数据传输、收费、可视化等。
与更广泛的开源生态系统协同合作
数据库开发的另一个趋势是利用数据库与其他云原生基础设施之间的协同作用。就 Milvus 而言,它依赖于一些开源系统。例如,Milvus 使用etcd来存储元数据。它还采用了微服务架构中使用的一种异步服务对服务通信方式--消息队列,可以帮助导出增量数据。
未来,我们希望将 Milvus 构建在Spark或Tensorflow 等人工智能基础设施之上,并将 Milvus 与流引擎集成,从而更好地支持统一的流处理和批处理,满足 Milvus 用户的各种需求。
Milvus 2.0 的设计原则
作为我们的下一代云原生向量数据库,Milvus 2.0 围绕以下三个原则构建。
日志即数据
数据库中的日志会连续记录对数据所做的所有更改。如下图所示,从左到右依次是 "旧数据 "和 "新数据"。日志按时间顺序排列。Milvus 有一个全局计时器机制,分配一个全局唯一且自动递增的时间戳。
日志
在 Milvus 2.0 中,日志代理是系统的主干:所有数据插入和更新操作都必须经过日志代理,工作节点通过订阅和消费日志来执行 CRUD 操作。
表和日志的双重性
表和日志都是数据,它们只是两种不同的形式。表是有界数据,而日志是无界数据。日志可以转换成表格。就 Milvus 而言,它使用 TimeTick 的处理窗口聚合日志。根据日志顺序,多个日志被聚合成一个称为日志快照的小文件。然后,这些日志快照被组合成一个段,可单独用于负载平衡。
日志持久性
日志持久性是许多数据库面临的棘手问题之一。分布式系统中的日志存储通常取决于复制算法。
与Aurora、HBase、Cockroach DB 和TiDB 等数据库不同,Milvus 开创性地引入了发布-订阅(pub/sub)系统,用于日志存储和持久化。发布/订阅系统类似于Kafka或Pulsar 中的消息队列。系统内的所有节点都可以使用日志。在 Milvus 中,这种系统被称为日志代理。有了日志代理,日志就能与服务器解耦,确保 Milvus 本身是无状态的,并能更好地从系统故障中快速恢复。
日志代理
为可扩展的相似性搜索建立向量数据库
Milvus 建立在流行的向量搜索库(包括 Faiss、ANNOY、HNSW 等)之上,专为在包含数百万、数十亿甚至数万亿向量的密集向量数据集上进行相似性搜索而设计。
独立和集群
Milvus 提供两种部署方式--单机或集群。在 Milvus 单机版中,由于所有节点都部署在一起,因此我们可以把 Milvus 看作一个单独的进程。目前,Milvus Standalone 依赖 MinIO 和 etcd 进行数据持久化和元数据存储。在未来的版本中,我们希望取消这两个第三方依赖,以确保 Milvus 系统的简洁性。Milvus 集群包括八个微服务组件和三个第三方依赖项:MinIO、etcd 和 Pulsar。Pulsar 充当日志代理并提供日志发布/子服务。
单机和集群
Milvus 架构的基本骨架
Milvus 将数据流与控制流分开,分为四层,在可扩展性和灾难恢复方面相互独立。
Milvus 架构
访问层
访问层是系统的门面,向外界展示客户端连接的端点。它负责处理客户端连接、进行静态验证、对用户请求进行基本动态检查、转发请求、收集并向客户端返回结果。代理本身是无状态的,通过负载均衡组件(Nginx、Kubernetess Ingress、NodePort 和 LVS)向外部世界提供统一的访问地址和服务。Milvus 采用大规模并行处理(MPP)架构,代理在全局聚合和后处理后返回从工作节点收集的结果。
协调器服务
协调器服务是系统的大脑,负责集群拓扑节点管理、负载平衡、时间戳生成、数据声明和数据管理。有关每个协调器服务功能的详细说明,请阅读Milvus 技术文档。
工作节点
工作节点或执行节点充当系统的中枢,执行协调器服务发出的指令和代理启动的数据操作语言(DML)命令。Milvus 中的工作节点类似于Hadoop 中的数据节点或 HBase 中的区域服务器。每种类型的工作节点都对应一个协调服务。有关每个工作节点功能的详细解释,请阅读Milvus 技术文档。
存储
存储是 Milvus 的基石,负责数据持久化。存储层分为三个部分:
- 元存储:负责存储 Collections Schema、节点状态、消息消耗检查点等元数据的快照。Milvus 依靠 etcd 实现这些功能,Etcd 还承担服务注册和健康检查的责任。
- 日志代理:支持回放的发布/子系统,负责流式数据持久化、可靠的异步查询执行、事件通知和返回查询结果。当节点执行宕机恢复时,日志代理通过日志代理回放确保增量数据的完整性。Milvus 集群使用 Pulsar 作为日志代理,而独立模式则使用 RocksDB。Kafka 和 Pravega 等流存储服务也可用作日志代理。
- 对象存储:存储日志快照文件、标量/向量索引文件和中间查询处理结果。Milvus 支持AWS S3和Azure Blob,以及轻量级开源对象存储服务MinIO。由于对象存储服务的访问延迟和每次查询计费较高,Milvus 将很快支持基于内存/SSD 的缓存池和冷热数据分离,以提高性能并降低成本。
数据模型
数据模型组织数据库中的数据。在 Milvus 中,所有数据都是按 Collections、shard、partition、segment 和 entity 组织的。
数据模型 1
Collections
在 Milvus 中,Collection 可以比作关系存储系统中的表。Collections 是 Milvus 中最大的数据单元。
分区
为了在写入数据时充分利用集群的并行计算能力,Milvus 中的 Collection 必须将数据写入操作分散到不同的节点上。默认情况下,一个 Collections 包含两个分片。根据你的数据集容量,你可以在一个 Collection 中拥有更多的分片。Milvus 使用主密钥散列方法进行分片。
分区
一个分区中也有多个分区。Milvus 中的分区指的是在一个 Collections 中标有相同标签的一组数据。常见的分区方法包括按日期、性别、用户年龄等进行分区。创建分区有利于查询过程,因为可以通过分区标签过滤大量数据。
相比之下,分片更注重写入数据时的扩展能力,而分区更注重提高读取数据时的系统性能。
数据模型 2
分区
每个分区中都有多个小段。段是 Milvus 系统调度的最小单位。分段有两种类型,即增长分段和封存分段。增长段由查询节点订阅。Milvus 用户不断将数据写入成长段。当增长段的大小达到上限(默认为 512 MB)时,系统将不允许向该增长段写入额外数据,从而封存该段。索引建立在封存段上。
要实时访问数据,系统会读取增长数据段和密封数据段中的数据。
实体
每个数据段都包含大量实体。Milvus 中的实体相当于传统数据库中的一行。每个实体都有一个唯一的主键字段,也可以自动生成。实体还必须包含时间戳(ts)和向量字段--这是 Milvus 的核心。
关于深入研究系列
随着 Milvus 2.0正式宣布全面上市,我们精心策划了这个 Milvus 深度剖析系列博客,对 Milvus 架构和源代码进行深入解读。本系列博客涉及的主题包括
- 非结构化数据需要完整的基础软件栈
- Milvus 2.0 的设计原则
- 关于深入研究系列
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word