Skip to content

Architecture

原文链接:https://www.yuque.com/yangguangfanxing/nmhuv1/dqnzk9areuc1s1nd

回顾:向量数据库的基本任务

向量数据库可以概括为:

基本流程:

  1. 将原始数据通过 embedding model 转换为向量
  2. 将向量 与相关属性存入数据库。
  3. 查询时将 query 转换为查询向量
  4. 在数据库中寻找 的最近邻。

主要操作包括 insert、delete、update、get by key,以及 kNN、range query、filtered query、multi-vector query 和 reranking。

ANNS Index 回顾

ANNS index 在多个指标之间权衡:recall、latency、throughput、memory 和 update cost。

常见索引:

类型代表方法
表 / 聚类结构Flat, IVF
树结构ANNOY
图结构HNSW, DiskANN
哈希结构LSH

索引还可叠加 SQ、PQ 等量化方法,或组成 IVFPQ、IVF-HNSW、IVF+HNSW+PQ 等组合索引。为了提供新鲜结果,系统还需要 freshness layer;为了避免重建,可使用 SPFresh、FreshDiskANN 等 updatable index。

逻辑 VDBMS 组件

一个概念架构:

组件作用
API接收插入、查询、删除等请求
Planner生成查询计划
Executor执行查询计划
Query processor管理查询处理逻辑
Liveness管理更新可见性、freshness、tombstone
Index支持 ANN 搜索
Storage manager管理持久化存储
Storage保存向量、属性、索引、日志

生产系统还会包含 monitoring、backup、orchestration、inference、plugin storage/index、multi-tenancy、security/compliance、coordination 等模块。

早期架构:Vearch

Vearch 是较早的分布式向量搜索系统,采用定制化分布式架构:sharding、ingest queue、文件存储,并服务过生产系统。

1. 查询架构

Vearch 的搜索子系统是层级结构:

查询流程:

  1. 查询到达 frontend。
  2. Frontend 选择 blender 进行负载均衡。
  3. Blender 提取特征,联系所有 brokers。
  4. Broker 联系每个 shard 的一个 searcher。
  5. Searcher 执行局部 IVF 搜索。
  6. Broker 和 blender 逐级聚合结果。

Blender 是搜索子系统的顶层调度与协调中心,其核心角色类似于一个分布式查询的“大脑”或“指挥者”。

在 Vearch 系统中,Blender 是搜索子系统的顶层调度与协调中心,其核心角色类似于一个分布式查询的“大脑”或“指挥者”。它的主要功能可以概括为以下几点:

  1. 查询接收与分发
  • 接收查询:Blender 接收来自前端服务的用户查询请求。
  • 任务分发:它将一个完整的查询请求(如一个 k-近邻搜索)拆解,并并行地分发给下一层的多个 Broker 节点。图中展示了一个 Blender 可以连接多个 (m个) Broker,从而实现并行处理,这是实现高性能搜索的关键。
  1. 结果聚合与融合
  • 结果收集:各个 Broker 在接收到来自下层 Searcher 的搜索结果后,会将结果返回给 Blender。
  • 融合排序:Blender 负责聚合来自所有 Broker 的候选结果。它需要对所有结果进行去重、合并,并执行最终的全局排序(例如,根据距离分数重新排序),从而从海量候选集中筛选出最精确的 Top-K 个结果。
  1. 负载均衡与高可用

架构图中明确标注了存在 k 个 Blender 实例。这意味着:

  • 负载均衡:前端请求可以均匀地分发到不同的 Blender 实例上,避免单点瓶颈,提高系统的整体吞吐量。
  • 高可用性:如果一个 Blender 实例发生故障,其他实例可以接管其工作,保证搜索服务不中断。
  1. 对外提供统一接口
  • Blender 对前端(Frontend)屏蔽了底层复杂的分布式搜索细节。前端只需要与 Blender 交互,无需关心请求被发给了多少个 Broker 或 Searcher。

2. 索引与更新

Vearch 使用 IVF:每个 cluster 维护向量列表,并用 end position table 加速插入。每个 searcher 管理一个 shard,索引驻留在 searcher RAM 中,属性存储在 forward index 中。

更新方面同时维护 full index 和 real-time index:

  • full index:夜间 ingest buffer,每周重建完整索引。
  • real-time index:插入追加到最近 cluster,更新/删除通过 bitmap 标记 valid/invalid。

简单比喻:把整个索引库想象成一座中央图书馆的所有藏书目录。

  • End Position Table每个书架内部的索引卡,告诉你哪个位置是空的可以放新书。
  • Shard Index Vertical 是建立了多个完全相同的、分布在不同城区的社区分馆。每个分馆都有一套完整的藏书目录。市民(查询请求)可以就近去任何一个分馆查找,分馆的目录就在管理员手边(内存中),查找速度极快。如果某个分馆关门了,市民可以去另一个分馆。
End Position Table(结束位置表)

这是一个优化数据插入性能的机制,与倒排文件 的数据结构紧密相关。

它要解决什么问题?在一个标准的倒排索引中,每个聚类中心对应的向量列表通常是顺序存储的。当需要插入一个新向量时,理想的情况是把它直接追加到这个列表的末尾。但如果每个列表是紧密连续存储的,插入到中间位置会导致其后的所有数据都需要向后移动,这是非常低效的。

它是如何工作的?

  1. 预留空间:系统为每个聚类的向量列表预先分配一块固定大小的连续存储空间(可以想象成一个“槽位数组”)。
  2. 记录指针:“结束位置表”本质上就是一个指针数组,它记录了每个聚类列表中当前最后一个有效向量的位置(即“结束位置”)。
  3. 高效插入:当一个新向量被分配到某个聚类时,系统只需:

带来的好处:

  • O(1)复杂度插入:插入操作变成了简单的追加和指针更新,避免了大规模的数据移动。
  • 保持数据局部性:同一个聚类的向量仍然存储在相对连续的内存/磁盘空间中,有利于缓存和批量读取,这对后续的快速搜索至关重要。

简单比喻:就像一个图书馆为每个主题(聚类)的书架预留了一整排空位。图书管理员手边有一个“结束位置表”,记录了每个主题书架最后一本书的位置。新书到来时,管理员查表,把书放到下一个空位,然后更新一下表格即可,无需移动其他书架的书。

Shard Index Vertical(垂直分片索引)

这是一个数据分布与高可用性的架构设计,用于构建分布式向量检索服务。

“Vertical”(垂直)在这里的含义是什么?

  • 水平分片:通常是指将数据本身按行或按范围切分,分布到不同机器上。例如,将用户ID 1-1000W的数据放在分片A,1000W-2000W的数据放在分片B。
  • 垂直分片:更准确的描述是“基于功能或副本的分片”。它是指整个完整的索引被复制成多个相同的副本(分片),每个副本作为一个独立的服务单元,共同来承载查询请求。这是一种“全量数据,多副本服务”的模式。

它是如何构建和工作的?

  1. 分片创建:整个向量索引(包含所有聚类和向量)被逻辑上划分为多个分片。图中显示,每个分片都有多个副本,目的是实现高可用负载均衡

  2. 索引加载:每个搜索器 进程,在内存中加载并持有一个完整分片的索引数据。这正是“One shard per searcher”的含义。这使得查询延迟极低,因为所有比较都在内存中进行。

  3. 路由与查询

  4. 前向索引存储:搜索器不仅存储向量索引,还存储每个向量对应的“产品属性”。这是一个标准的前向索引,键是向量ID或图片URL,值是属性(如标题、价格等)。当搜索到最近的向量后,可以立即“前向”查找并返回这些属性,无需再访问其他数据库,极大地提升了端到端的查询效率。

带来的好处:

  • 高可用:多副本机制避免了单点故障。
  • 高并发:多个搜索器副本可以同时处理针对同一分片的查询,提升了系统的整体吞吐量。
  • 低延迟:索引和属性全部在搜索器内存中,响应速度极快。
  • 可扩展:可以通过增加每个分片的副本来提升该分片的服务能力,或增加总分片数来水平扩展整体数据容量。

FULL INDEXREALTIME INDEX 的主要区别如下:

维度FULL INDEX (批量索引)REALTIME INDEX (实时索引)
1. 处理方式批量、周期性处理实时、原子性处理
2. 核心流程日志记录 -> 夜间定时导入 -> 周级重建对插入、更新、删除操作立即响应
3. 数据流数据先进入缓冲区,延迟处理数据直接处理,无缓冲延迟
4. 索引更新通过每周重建整个索引来生效通过立即追加到最近簇来生效
5. 属性与状态维护在重建阶段统一构建属性数据库和有效位图在每次操作时原子性地更新属性库和位图
6. 主要目标保证索引的全局最优数据一致性,适用于可容忍延迟的数据刷新保证数据的近实时可用性快速可见,适用于需要即时响应的场景
7. 适用场景非频繁更新、可接受小时/天级延迟的批量数据(如后台分析、定时报表)需要支持在线插入、更新、删除操作的交互式应用(如商品搜索、实时推荐)

FULL INDEX批量导向、延迟生效的离线处理模式;REALTIME INDEX操作导向、立即生效的在线处理模式。

3. 性能与问题

,20 个 searchers + 6 个 blenders/brokers,平均延迟约 100ms,P99 超过 300ms,最大延迟 2.1s,吞吐约 1800 QPS。

主要问题:延迟高、吞吐低、资源消耗大、real-time index 干扰吞吐、每周重建、节点角色固定导致弹性差。

现代架构:Manu / Milvus 2.0

Manu 是 Milvus 2.0 背后的架构思想,采用 service-oriented / disaggregated design。

核心特点:

  • 不同功能由独立 workers 执行。
  • 组件通过 distributed log 连接。
  • coordinators 负责调度和元数据。
  • index、search、storage 可独立扩缩容。
  • 支持多种索引,如 FAISS、HNSWlib、ANNOY。

需求如何转化为架构特性

需求架构特性
Row-level ACID 足够支持单行 insert/update/delete/query
一致性不能只有 strong/eventual提供 bounded staleness / delta consistency
GPU、索引、存储成本不同功能解耦,独立扩缩容

Delta consistency 允许用户指定最大 stale 时间 ,比强一致和最终一致更灵活。

数据组织

Manu 存储:

  • user data:key / ID + vector + attributes
  • metadata:sequence number

数据按 primary key 静态划分为 shard:

Shard 再划分为 segment:

Segment含义
Growing segmentappend-only,正在写入
Sealed segmentread-only,已封存并可建索引

segment 在达到 512MB 或经过 10 秒后 seal 并建索引。Growing segment 还会划分为 slice,例如每个 slice 有 10K vectors,完整 slice 可建 IVF 以支持搜索。

Manu 架构分层

1. Storage layer

对象存储(S3、MinIO、文件)保存 columnar binlog、vectors、attributes、index、segment mapping SSTables。

KV store(etcd)保存 metadata、状态信息,并供 coordinators 使用。

2. Worker nodes

Workers 是 stateless 的,彼此无直接协调,可独立扩缩容:

Worker作用
Query node执行搜索
Data node将排队更新持久化为 binlog
Index node构建索引

BinlogBinary Log 的缩写,即二进制日志。它是一种顺序记录所有数据变更(插入、更新、删除)事件的只追加文件。你可以把它理解为系统数据变化的“完整磁带录像”。

3. Coordinators

Coordinator职责
Root coord管理系统、集群、collection,提供 timestamp oracle service
Data coord管理 data nodes 和 segment 路径
Query coord管理 query nodes,将 segment 分配给 query nodes
Index coord管理 index nodes 和 index metadata

4. Access layer

通常是 stateless proxy:接收请求、用 metadata cache 验证、分发给 workers、聚合结果并返回。

Log Backbone:WAL 作为系统骨架

Manu 使用 Write-Ahead Log 连接组件,通常由 Kafka / Pulsar 实现。

所有状态变化都发布到 WAL:insert vector、delete vector、create/delete collection、metadata/control-plane changes。节点订阅 WAL;查询请求通常绕过 WAL,直接走查询路径。

优点:组件解耦、独立扩缩容、统一 data plane 和 control plane、提供一致时间语义。

插入路径:从 insert 到 index

插入请求流程:

  1. Proxy 验证请求。
  2. Timestamp oracle 分配 LSN (Log Sequence Number) / sequence number。
  3. 根据 key 分配 segment。
  4. 写入 WAL。
  5. 将 key-segment mapping 写入 LSM tree。
  6. insert 可 acknowledge,但后台流程继续。

Key 到 shard 的映射可写为:

之后:

  • Query node 订阅 WAL,更新 RAM 中 growing segment,并按需为 slice 建轻量索引。
  • Data node 订阅 WAL,转换为 columnar binlog,写入 object store;如果 segment 满足条件,则 seal 并通知 coordinator。

Slice 的概念与作用:

  • 层级关系:在 Manu 中,数据首先被写入到处于 Growing 状态的 Segment 里。为了更高效地管理和查询这些数据,系统会将一个大的 Segment 切分成多个较小的 Slice。通常情况下,一个 Slice 大约包含 1万条向量数据
  • 核心目的:将大数据块切分成小切片,主要是为了支持增量索引构建资源隔离。每个小切片可以独立地被处理,而不会阻塞整个 Segment 的写入操作。

为了更直观地理解 Slice 索引的定位,可以参考 Manu 中索引的三种状态:

数据状态索引类型存储位置与特点
Growing SegmentSlice Index (临时索引)仅存在于 Query Node 的内存中。随数据写入动态构建,提供低延迟查询。
Sealed SegmentFull Index (完整索引)持久化存储在 对象存储(如 AWS S3)中。数据冻结后离线构建,供长期查询使用。
异常/迁移状态无索引 (Binlog)依赖底层的 Binlog 数据进行回放和查询。

简而言之,Slice 是 Manu 为了平衡写入速度查询性能而设计的“分块”策略,而 Build Slice Index 则是让系统在面对流式数据时,依然能保持亚秒级响应能力的关键优化手段。

  • Data coordinator 通知 index coordinator;
  • index node 加载 binlog、构建索引、存入 storage;
  • query coordinator 再通知 query node 加载索引。

复杂架构的收益

虽然路径复杂,但收益明显:

  • WAL 写入后即可快速确认 insert。
  • 读写路径基本解耦。
  • WAL + growing segment 构成 liveness layer。
  • WAL 和 binlog 提供 fault tolerance。
  • log 提供一致时间语义。
  • 各功能组件可独立扩缩容。
  • 新分析任务可通过订阅 WAL / binlog 接入。

查询路径

简单查询流程:

  1. Proxy 根据 cached metadata 联系 query nodes。
  2. Query nodes 并行运行 kNN。
  3. 每个 query node 搜索自己负责的所有 segments。
  4. 每个 query node 返回局部 top-k。
  5. Proxy 聚合全局 top-k。
  6. 返回结果。

本质是分布式 top-k merge,但 Manu 通过服务解耦使扩缩容更灵活。

Delta Consistency

设查询时间为 ,用户指定最大 stale 时间为 ,query node 已接收的最新更新时间为

系统要求:查询必须包含 之前的所有更新; 之间的更新可包含也可不包含。

判断规则:

  • ,说明必须包含的更新还没到,查询需要等待。
  • ,说明相关更新已到,可以执行查询。

系统通过 LSN、watermarks、heartbeats 跟踪每个 channel 的更新时间

其他架构考虑

  • Full index rebuild:从 data coord 获取 segment 路径,分配 index nodes,构建后通知 query nodes 加载。
  • Delete/update:通常通过 per-segment bitmap 或 tombstone 标记,后续在 rebuild/compaction 时清理。
  • Segment reassignment:用于扩缩容、负载均衡、故障恢复,由 query coordinator 管理。

  • 论文还讨论 filtered queries、multi-vector queries、Bayesian optimization for index configuration、SIMD/GPU 优化、time travel、fault tolerance、monitoring 和 load balancing。

性能结果

单机性能

配置:单节点 EC2 m5.4xlarge,16 vCPU、64GB RAM,Recall@50,SIFT 10M 和 DEEP 10M。结论是 Manu 单机性能较好,SIMD 和 CPU 优化很重要,磁盘系统在该实验下较慢。

Elasticity

配置:4 nodes,包括 2 data nodes、1 query node、1 index node,SIFT 100M,Recall@50 > 80%。系统可根据延迟调整 query nodes:低于 100ms 可减少,高于 150ms 可增加。

SIFT 的全称是 Scale-Invariant Feature Transform(尺度不变特征变换)

  • 来源:它最初是由计算机视觉专家 David Lowe 在 1999 年提出的算法,用于从图像中提取具有尺度、旋转和光照不变性的特征点。
  • 数据结构:提取出来的特征通常被表示为高维向量。最经典的 SIFT1M 数据集包含 100万条 128维 的向量。
  • SIFT 100M 具体指的是包含 1亿条(100 Million)128维 向量的数据集

Scalability

Segmenting 允许随着数据集大小和 query nodes 数量近似线性扩展,即使使用 HNSW 这类图索引也能扩展。若想更好利用图索引的亚线性复杂度,可以增大 segment size。

Index building

单个 index node 在 EC2 m5.4xlarge 上可在 1 小时内处理 100M vectors。由于查询和建索引路径解耦,对查询影响较小,但可能影响较小 下的 freshness。

总结

本讲展示了向量数据库架构从早期定制化系统 Vearch,到现代解耦式系统 Manu / Milvus 2.0 的演进。

核心思想:

  1. 向量数据库不只是 ANN index,还包括 storage、liveness、WAL、coordinators、workers、access layer。
  2. 早期系统角色固定、扩展性差;现代系统倾向于 disaggregated architecture。
  3. WAL 是连接写入、存储、索引、查询和控制面的骨架。
  4. Segmenting、growing/sealed segment、freshness layer 是支持动态数据的关键。
  5. Delta consistency 用 给用户提供可调的新鲜度。
  6. 独立扩缩容 query、data、index 组件,是现代 VDBMS 的重要架构优势。

向量数据库结合了 RDBMS、分布式系统和高维检索算法,是仍在快速发展的领域。

阅读资料: MANU: Milvus 2.0

用心记录,持续成长