引言:AI 原生时代的向量数据库
在人工智能应用爆炸式增长的今天,非结构化数据(如图片、视频、音频、文本)的处理成为核心挑战。传统的关系型数据库擅长处理结构化数据,但对高维向量数据的存储、索引和检索却力不从心。Milvus 作为一款开源的向量数据库,专为 AI 应用而生,其架构设计深刻体现了"AI 原生"的理念------从底层存储到上层查询,都围绕向量数据的高效处理而构建。
本文将深入剖析 Milvus 的架构设计,揭示其如何将向量索引、分布式存储与检索、流批一体等复杂技术融合,构建出一个高性能、可扩展的 AI 数据基础设施。
1. 整体架构概览:解耦与协同
Milvus 采用云原生、分层解耦的架构设计,其核心思想是将系统的不同职责(存储、计算、索引、协调)分离,通过标准接口进行通信。这种设计带来了极高的灵活性和可扩展性。
1.1 系统组件构成
Milvus 2.x 版本主要由以下核心组件构成:
- 接入层 (Access Layer): 提供统一的 RESTful 和 gRPC 接口,负责接收客户端请求,并进行负载均衡和协议转换。
- 协调服务 (Coordinator Service) : 系统的"大脑",负责集群元数据管理、负载均衡、任务调度与故障恢复。它进一步细分为:
- 根协调器 (Root Coordinator): 管理数据库、集合、分区的元数据(DDL 操作)。
- 查询协调器 (Query Coordinator): 管理查询节点的负载均衡与故障转移。
- 数据协调器 (Data Coordinator): 管理数据节点的数据刷新、压缩与均衡。
- 索引协调器 (Index Coordinator): 管理索引的构建与维护任务。
- 工作节点 (Worker Nodes) : 系统的"四肢",负责实际的数据处理:
- 查询节点 (Query Node): 执行向量和标量数据的检索(查询)操作。
- 数据节点 (Data Node): 处理数据的插入、删除,并将日志数据持久化到对象存储。
- 索引节点 (Index Node): 专门负责构建和管理向量索引。
- 存储层 (Storage) : 持久化数据的基石,采用日志即数据 (Log-as-Data) 模型:
- 元存储 (Meta Store): 通常使用 etcd 或 MySQL,存储集合、分区、索引结构等元数据。
- 对象存储 (Object Storage): 如 AWS S3、Azure Blob、MinIO,用于持久化向量和标量数据文件。
- 消息队列 (Message Queue): 如 Pulsar、Kafka,作为系统的发布-订阅日志总线,用于实时数据传输与同步。
#mermaid-svg-MCJdo1qO3i4oCbHk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MCJdo1qO3i4oCbHk .error-icon{fill:#552222;}#mermaid-svg-MCJdo1qO3i4oCbHk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MCJdo1qO3i4oCbHk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MCJdo1qO3i4oCbHk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MCJdo1qO3i4oCbHk .marker.cross{stroke:#333333;}#mermaid-svg-MCJdo1qO3i4oCbHk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MCJdo1qO3i4oCbHk p{margin:0;}#mermaid-svg-MCJdo1qO3i4oCbHk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk .cluster-label text{fill:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk .cluster-label span{color:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk .cluster-label span p{background-color:transparent;}#mermaid-svg-MCJdo1qO3i4oCbHk .label text,#mermaid-svg-MCJdo1qO3i4oCbHk span{fill:#333;color:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk .node rect,#mermaid-svg-MCJdo1qO3i4oCbHk .node circle,#mermaid-svg-MCJdo1qO3i4oCbHk .node ellipse,#mermaid-svg-MCJdo1qO3i4oCbHk .node polygon,#mermaid-svg-MCJdo1qO3i4oCbHk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MCJdo1qO3i4oCbHk .rough-node .label text,#mermaid-svg-MCJdo1qO3i4oCbHk .node .label text,#mermaid-svg-MCJdo1qO3i4oCbHk .image-shape .label,#mermaid-svg-MCJdo1qO3i4oCbHk .icon-shape .label{text-anchor:middle;}#mermaid-svg-MCJdo1qO3i4oCbHk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MCJdo1qO3i4oCbHk .rough-node .label,#mermaid-svg-MCJdo1qO3i4oCbHk .node .label,#mermaid-svg-MCJdo1qO3i4oCbHk .image-shape .label,#mermaid-svg-MCJdo1qO3i4oCbHk .icon-shape .label{text-align:center;}#mermaid-svg-MCJdo1qO3i4oCbHk .node.clickable{cursor:pointer;}#mermaid-svg-MCJdo1qO3i4oCbHk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MCJdo1qO3i4oCbHk .arrowheadPath{fill:#333333;}#mermaid-svg-MCJdo1qO3i4oCbHk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MCJdo1qO3i4oCbHk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MCJdo1qO3i4oCbHk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MCJdo1qO3i4oCbHk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MCJdo1qO3i4oCbHk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MCJdo1qO3i4oCbHk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MCJdo1qO3i4oCbHk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MCJdo1qO3i4oCbHk .cluster text{fill:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk .cluster span{color:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MCJdo1qO3i4oCbHk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MCJdo1qO3i4oCbHk rect.text{fill:none;stroke-width:0;}#mermaid-svg-MCJdo1qO3i4oCbHk .icon-shape,#mermaid-svg-MCJdo1qO3i4oCbHk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MCJdo1qO3i4oCbHk .icon-shape p,#mermaid-svg-MCJdo1qO3i4oCbHk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MCJdo1qO3i4oCbHk .icon-shape .label rect,#mermaid-svg-MCJdo1qO3i4oCbHk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MCJdo1qO3i4oCbHk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MCJdo1qO3i4oCbHk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MCJdo1qO3i4oCbHk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 存储层
工作节点
协调服务
接入层
客户端
查询流
SDK/CLI
Web UI
代理服务
根协调器
查询协调器
数据协调器
索引协调器
查询节点
数据节点
索引节点
元存储 etcd
对象存储 S3
消息队列 Pulsar
图:Milvus 2.x 架构组件与数据流示意图
1.2 数据流与读写路径
理解架构的关键是追踪数据的生命周期:
- 写入路径:客户端插入数据 -> 代理服务 -> 消息队列(作为预写日志 WAL)-> 数据节点消费日志 -> 数据节点将数据打包成段(Segment)并上传至对象存储 -> 通知协调器更新元数据。
- 查询路径:客户端发起查询 -> 代理服务 -> 查询协调器分配查询节点 -> 查询节点从对象存储加载相关数据段和索引 -> 执行检索运算 -> 返回结果。
这种将计算与存储分离 、日志驱动的架构,使得 Milvus 能够独立扩展计算资源(节点)和存储资源,并天然支持数据的多副本与高可用。
#mermaid-svg-yslCzNePoqS0g9NU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yslCzNePoqS0g9NU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yslCzNePoqS0g9NU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yslCzNePoqS0g9NU .error-icon{fill:#552222;}#mermaid-svg-yslCzNePoqS0g9NU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yslCzNePoqS0g9NU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yslCzNePoqS0g9NU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yslCzNePoqS0g9NU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yslCzNePoqS0g9NU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yslCzNePoqS0g9NU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yslCzNePoqS0g9NU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yslCzNePoqS0g9NU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yslCzNePoqS0g9NU .marker.cross{stroke:#333333;}#mermaid-svg-yslCzNePoqS0g9NU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yslCzNePoqS0g9NU p{margin:0;}#mermaid-svg-yslCzNePoqS0g9NU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yslCzNePoqS0g9NU .cluster-label text{fill:#333;}#mermaid-svg-yslCzNePoqS0g9NU .cluster-label span{color:#333;}#mermaid-svg-yslCzNePoqS0g9NU .cluster-label span p{background-color:transparent;}#mermaid-svg-yslCzNePoqS0g9NU .label text,#mermaid-svg-yslCzNePoqS0g9NU span{fill:#333;color:#333;}#mermaid-svg-yslCzNePoqS0g9NU .node rect,#mermaid-svg-yslCzNePoqS0g9NU .node circle,#mermaid-svg-yslCzNePoqS0g9NU .node ellipse,#mermaid-svg-yslCzNePoqS0g9NU .node polygon,#mermaid-svg-yslCzNePoqS0g9NU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yslCzNePoqS0g9NU .rough-node .label text,#mermaid-svg-yslCzNePoqS0g9NU .node .label text,#mermaid-svg-yslCzNePoqS0g9NU .image-shape .label,#mermaid-svg-yslCzNePoqS0g9NU .icon-shape .label{text-anchor:middle;}#mermaid-svg-yslCzNePoqS0g9NU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yslCzNePoqS0g9NU .rough-node .label,#mermaid-svg-yslCzNePoqS0g9NU .node .label,#mermaid-svg-yslCzNePoqS0g9NU .image-shape .label,#mermaid-svg-yslCzNePoqS0g9NU .icon-shape .label{text-align:center;}#mermaid-svg-yslCzNePoqS0g9NU .node.clickable{cursor:pointer;}#mermaid-svg-yslCzNePoqS0g9NU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yslCzNePoqS0g9NU .arrowheadPath{fill:#333333;}#mermaid-svg-yslCzNePoqS0g9NU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yslCzNePoqS0g9NU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yslCzNePoqS0g9NU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yslCzNePoqS0g9NU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yslCzNePoqS0g9NU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yslCzNePoqS0g9NU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yslCzNePoqS0g9NU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yslCzNePoqS0g9NU .cluster text{fill:#333;}#mermaid-svg-yslCzNePoqS0g9NU .cluster span{color:#333;}#mermaid-svg-yslCzNePoqS0g9NU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yslCzNePoqS0g9NU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yslCzNePoqS0g9NU rect.text{fill:none;stroke-width:0;}#mermaid-svg-yslCzNePoqS0g9NU .icon-shape,#mermaid-svg-yslCzNePoqS0g9NU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yslCzNePoqS0g9NU .icon-shape p,#mermaid-svg-yslCzNePoqS0g9NU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yslCzNePoqS0g9NU .icon-shape .label rect,#mermaid-svg-yslCzNePoqS0g9NU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yslCzNePoqS0g9NU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yslCzNePoqS0g9NU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yslCzNePoqS0g9NU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询路径
写入路径
元数据同步
客户端插入数据
代理服务
消息队列
(预写日志 WAL)
数据节点消费日志
打包成段 Segment
上传至对象存储
通知协调器更新元数据
客户端发起查询
代理服务
查询协调器分配查询节点
查询节点加载数据段和索引
执行检索运算
返回结果
图:Milvus 数据写入与查询路径示意图
2. 存储引擎:日志即数据与分层存储
Milvus 的存储设计是其高可靠性与高性能的基石,核心是 "日志即数据 (Log-as-Data)" 模型。
2.1 日志即数据模型
- 所有数据变更首先作为一条日志记录发布到消息队列(如 Pulsar)。这条日志包含了插入、删除操作的完整信息。
- 数据节点作为日志的消费者,顺序消费这些日志,将其在内存中组装成有序的数据结构。
- 当积累的数据达到一定阈值(如时间、行数),数据节点会将这些数据持久化 到对象存储中,形成一个不可变的数据段 (Segment)。
- 元数据中会记录每个段对应的日志位置。因此,从任意时间点恢复数据,只需要从对应的日志位置开始重放即可。
这种设计带来了显著优势:
- 强一致性:通过日志的顺序性保证。
- 高吞吐写入:日志追加写入非常高效。
- 容灾与恢复:存储成本低廉的对象存储保存数据,消息队列保存操作日志,故障恢复速度快。
#mermaid-svg-MWXPtWo2kknPMnhs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MWXPtWo2kknPMnhs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MWXPtWo2kknPMnhs .error-icon{fill:#552222;}#mermaid-svg-MWXPtWo2kknPMnhs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MWXPtWo2kknPMnhs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MWXPtWo2kknPMnhs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MWXPtWo2kknPMnhs .marker.cross{stroke:#333333;}#mermaid-svg-MWXPtWo2kknPMnhs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MWXPtWo2kknPMnhs p{margin:0;}#mermaid-svg-MWXPtWo2kknPMnhs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MWXPtWo2kknPMnhs .cluster-label text{fill:#333;}#mermaid-svg-MWXPtWo2kknPMnhs .cluster-label span{color:#333;}#mermaid-svg-MWXPtWo2kknPMnhs .cluster-label span p{background-color:transparent;}#mermaid-svg-MWXPtWo2kknPMnhs .label text,#mermaid-svg-MWXPtWo2kknPMnhs span{fill:#333;color:#333;}#mermaid-svg-MWXPtWo2kknPMnhs .node rect,#mermaid-svg-MWXPtWo2kknPMnhs .node circle,#mermaid-svg-MWXPtWo2kknPMnhs .node ellipse,#mermaid-svg-MWXPtWo2kknPMnhs .node polygon,#mermaid-svg-MWXPtWo2kknPMnhs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MWXPtWo2kknPMnhs .rough-node .label text,#mermaid-svg-MWXPtWo2kknPMnhs .node .label text,#mermaid-svg-MWXPtWo2kknPMnhs .image-shape .label,#mermaid-svg-MWXPtWo2kknPMnhs .icon-shape .label{text-anchor:middle;}#mermaid-svg-MWXPtWo2kknPMnhs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MWXPtWo2kknPMnhs .rough-node .label,#mermaid-svg-MWXPtWo2kknPMnhs .node .label,#mermaid-svg-MWXPtWo2kknPMnhs .image-shape .label,#mermaid-svg-MWXPtWo2kknPMnhs .icon-shape .label{text-align:center;}#mermaid-svg-MWXPtWo2kknPMnhs .node.clickable{cursor:pointer;}#mermaid-svg-MWXPtWo2kknPMnhs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MWXPtWo2kknPMnhs .arrowheadPath{fill:#333333;}#mermaid-svg-MWXPtWo2kknPMnhs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MWXPtWo2kknPMnhs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MWXPtWo2kknPMnhs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MWXPtWo2kknPMnhs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MWXPtWo2kknPMnhs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MWXPtWo2kknPMnhs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MWXPtWo2kknPMnhs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MWXPtWo2kknPMnhs .cluster text{fill:#333;}#mermaid-svg-MWXPtWo2kknPMnhs .cluster span{color:#333;}#mermaid-svg-MWXPtWo2kknPMnhs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MWXPtWo2kknPMnhs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MWXPtWo2kknPMnhs rect.text{fill:none;stroke-width:0;}#mermaid-svg-MWXPtWo2kknPMnhs .icon-shape,#mermaid-svg-MWXPtWo2kknPMnhs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MWXPtWo2kknPMnhs .icon-shape p,#mermaid-svg-MWXPtWo2kknPMnhs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MWXPtWo2kknPMnhs .icon-shape .label rect,#mermaid-svg-MWXPtWo2kknPMnhs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MWXPtWo2kknPMnhs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MWXPtWo2kknPMnhs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MWXPtWo2kknPMnhs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据节点处理
是
否
故障恢复时
客户端写入请求
代理服务
消息队列
Pulsar/Kafka
消费日志
内存组装数据结构
达到阈值?
(时间/行数)
持久化为段 Segment
对象存储
S3/MinIO等
元存储
记录段-日志映射
从日志位置重放
图:日志即数据模型工作流程
2.2 分层存储:热数据、温数据、冷数据
Milvus 智能地管理数据在内存和磁盘间的流动:
- 增长段 (Growing Segment): 最新插入的数据驻留在内存中,属于"热数据",支持低延迟的插入和实时查询。
- 密封段 (Sealed Segment): 当增长段达到配置大小后,会被"密封",并触发索引构建。密封段会被持久化到对象存储。查询节点可以将其加载到内存或磁盘缓存中进行查询,属于"温数据"。
- 冷段: 访问频率极低的旧数据段,可能只存在于对象存储中,查询时按需加载,延迟较高。
查询节点通过 LRU (最近最少使用) 等策略管理内存中的段缓存,优化查询性能与资源利用率。
#mermaid-svg-zmm2f9NtcOz6qGoV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zmm2f9NtcOz6qGoV .error-icon{fill:#552222;}#mermaid-svg-zmm2f9NtcOz6qGoV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zmm2f9NtcOz6qGoV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zmm2f9NtcOz6qGoV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zmm2f9NtcOz6qGoV .marker.cross{stroke:#333333;}#mermaid-svg-zmm2f9NtcOz6qGoV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zmm2f9NtcOz6qGoV p{margin:0;}#mermaid-svg-zmm2f9NtcOz6qGoV defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-zmm2f9NtcOz6qGoV g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-zmm2f9NtcOz6qGoV g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-zmm2f9NtcOz6qGoV g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-zmm2f9NtcOz6qGoV g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-zmm2f9NtcOz6qGoV g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-zmm2f9NtcOz6qGoV .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-zmm2f9NtcOz6qGoV .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-zmm2f9NtcOz6qGoV .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-zmm2f9NtcOz6qGoV .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-zmm2f9NtcOz6qGoV .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-zmm2f9NtcOz6qGoV .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-zmm2f9NtcOz6qGoV .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-zmm2f9NtcOz6qGoV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zmm2f9NtcOz6qGoV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zmm2f9NtcOz6qGoV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zmm2f9NtcOz6qGoV .edgeLabel .label text{fill:#333;}#mermaid-svg-zmm2f9NtcOz6qGoV .label div .edgeLabel{color:#333;}#mermaid-svg-zmm2f9NtcOz6qGoV .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-zmm2f9NtcOz6qGoV .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-zmm2f9NtcOz6qGoV .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-zmm2f9NtcOz6qGoV .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-zmm2f9NtcOz6qGoV .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-zmm2f9NtcOz6qGoV .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zmm2f9NtcOz6qGoV .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zmm2f9NtcOz6qGoV #statediagram-barbEnd{fill:#333333;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zmm2f9NtcOz6qGoV .cluster-label,#mermaid-svg-zmm2f9NtcOz6qGoV .nodeLabel{color:#131300;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-zmm2f9NtcOz6qGoV .note-edge{stroke-dasharray:5;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-note text{fill:black;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram-note .nodeLabel{color:black;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagram .edgeLabel{color:red;}#mermaid-svg-zmm2f9NtcOz6qGoV #dependencyStart,#mermaid-svg-zmm2f9NtcOz6qGoV #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-zmm2f9NtcOz6qGoV .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zmm2f9NtcOz6qGoV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据插入
达到配置大小
索引构建完成
访问频率极低
GrowingSegment
驻留内存
实时查询
HotMemory
SealedSegment
持久化到对象存储
加载到查询节点缓存
温数据查询
WarmStorage
WarmCache
IndexedSegment
仅存对象存储
按需加载
冷数据查询
ColdStorage
OnDemand
ColdSegment
图:数据分层存储状态转换 - 热、温、冷数据生命周期
3. 向量索引核心:近似最近邻搜索的加速器
向量索引是向量数据库的"发动机",其目标是在数十亿甚至数百亿的高维向量中,快速找到与目标向量最相似的 Top-K 个结果。Milvus 集成了多种主流索引类型,适用于不同场景。
3.1 索引类型与算法
-
FLAT (暴力搜索)
- 原理:计算查询向量与集合中每个向量的距离(如 L2、内积)。
- 特点:100% 召回率,精度最高,但速度慢,适合小数据集(百万级以下)或作为精度验证的基准。
- 在 Milvus 中,FLAT 索引更多代表"无索引",直接使用原始向量数据。
-
IVF 系列 (倒排文件)
- IVF_FLAT : 最经典的聚类索引。先对全量向量进行 K-Means 聚类,得到
nlist个聚类中心。搜索时,先计算查询向量与所有聚类中心的距离,找到最近的nprobe个类簇,然后在这些类簇内的所有向量中进行暴力搜索。 - IVF_SQ8/IVF_PQ: 在 IVF 基础上,对每个类簇内的向量进行标量化(SQ)或乘积量化(PQ),大幅减少内存占用和距离计算成本,以轻微精度损失换取性能和资源的优化。
- 适用场景:兼顾精度和速度的通用场景,是 Milvus 中最常用的索引类型之一。
- IVF_FLAT : 最经典的聚类索引。先对全量向量进行 K-Means 聚类,得到
-
HNSW (可导航小世界图)
- 原理:构建一个多层图结构,上层是"高速公路",下层是"精细道路"。搜索从顶层开始,快速接近目标区域,然后逐层向下细化搜索。
- 特点:查询速度极快,尤其适合高维数据,但索引构建时间长,内存占用高。支持增量插入。
- 适用场景:对查询延迟要求极高,且数据量不是特别巨大的场景。
-
SCANN (基于图的索引)
- 原理:类似 HNSW 的图索引,但在索引构建和搜索策略上进行了优化。
- 特点:在千万到亿级数据集上,能在查询性能、内存占用和索引构建时间之间取得更好的平衡。
-
DISKANN (磁盘ANN)
- 原理:专为磁盘级向量检索设计。它将图索引与 SSD 的访问特性结合,索引完全存储在磁盘上,仅将搜索路径上的少量节点加载到内存。
- 特点:支持远超内存容量的大型数据集(百亿级),查询速度比纯内存索引慢,但比传统方法快几个数量级。
- 适用场景:超大规模向量数据库,成本敏感型应用。
#mermaid-svg-z2dMGRarzmunaFXi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-z2dMGRarzmunaFXi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-z2dMGRarzmunaFXi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-z2dMGRarzmunaFXi .error-icon{fill:#552222;}#mermaid-svg-z2dMGRarzmunaFXi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-z2dMGRarzmunaFXi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-z2dMGRarzmunaFXi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-z2dMGRarzmunaFXi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-z2dMGRarzmunaFXi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-z2dMGRarzmunaFXi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-z2dMGRarzmunaFXi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-z2dMGRarzmunaFXi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-z2dMGRarzmunaFXi .marker.cross{stroke:#333333;}#mermaid-svg-z2dMGRarzmunaFXi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-z2dMGRarzmunaFXi p{margin:0;}#mermaid-svg-z2dMGRarzmunaFXi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-z2dMGRarzmunaFXi .cluster-label text{fill:#333;}#mermaid-svg-z2dMGRarzmunaFXi .cluster-label span{color:#333;}#mermaid-svg-z2dMGRarzmunaFXi .cluster-label span p{background-color:transparent;}#mermaid-svg-z2dMGRarzmunaFXi .label text,#mermaid-svg-z2dMGRarzmunaFXi span{fill:#333;color:#333;}#mermaid-svg-z2dMGRarzmunaFXi .node rect,#mermaid-svg-z2dMGRarzmunaFXi .node circle,#mermaid-svg-z2dMGRarzmunaFXi .node ellipse,#mermaid-svg-z2dMGRarzmunaFXi .node polygon,#mermaid-svg-z2dMGRarzmunaFXi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-z2dMGRarzmunaFXi .rough-node .label text,#mermaid-svg-z2dMGRarzmunaFXi .node .label text,#mermaid-svg-z2dMGRarzmunaFXi .image-shape .label,#mermaid-svg-z2dMGRarzmunaFXi .icon-shape .label{text-anchor:middle;}#mermaid-svg-z2dMGRarzmunaFXi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-z2dMGRarzmunaFXi .rough-node .label,#mermaid-svg-z2dMGRarzmunaFXi .node .label,#mermaid-svg-z2dMGRarzmunaFXi .image-shape .label,#mermaid-svg-z2dMGRarzmunaFXi .icon-shape .label{text-align:center;}#mermaid-svg-z2dMGRarzmunaFXi .node.clickable{cursor:pointer;}#mermaid-svg-z2dMGRarzmunaFXi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-z2dMGRarzmunaFXi .arrowheadPath{fill:#333333;}#mermaid-svg-z2dMGRarzmunaFXi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-z2dMGRarzmunaFXi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-z2dMGRarzmunaFXi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z2dMGRarzmunaFXi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-z2dMGRarzmunaFXi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z2dMGRarzmunaFXi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-z2dMGRarzmunaFXi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-z2dMGRarzmunaFXi .cluster text{fill:#333;}#mermaid-svg-z2dMGRarzmunaFXi .cluster span{color:#333;}#mermaid-svg-z2dMGRarzmunaFXi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-z2dMGRarzmunaFXi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-z2dMGRarzmunaFXi rect.text{fill:none;stroke-width:0;}#mermaid-svg-z2dMGRarzmunaFXi .icon-shape,#mermaid-svg-z2dMGRarzmunaFXi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z2dMGRarzmunaFXi .icon-shape p,#mermaid-svg-z2dMGRarzmunaFXi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-z2dMGRarzmunaFXi .icon-shape .label rect,#mermaid-svg-z2dMGRarzmunaFXi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z2dMGRarzmunaFXi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-z2dMGRarzmunaFXi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-z2dMGRarzmunaFXi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 小数据集
(< 百万)
中等数据集
(百万~千万)
极高
平衡
优化资源
超大规模
(十亿~百亿)
是
否
选择向量索引
数据规模?
FLAT
暴力搜索
100%召回率
查询延迟要求?
HNSW
极快查询
高内存占用
IVF系列
兼顾精度速度
通用首选
SCANN
性能平衡
千万~亿级
DISKANN
磁盘级检索
成本敏感
需要压缩?
IVF_PQ/SQ8
内存优化
轻微精度损失
IVF_FLAT
经典聚类
精度优先
确定索引类型
图:Milvus 索引类型选择决策树
3.2 索引构建与管理
- 异步构建:索引构建由专门的索引节点异步执行,不影响数据插入的可用性。
- 段级索引:索引以数据段为单位构建。每个密封段都会独立构建自己的索引文件,并上传到对象存储。
- 自动合并:数据协调器会定期触发"压缩"操作,将多个小段合并成一个大段,并重建索引,以优化查询性能。
4. 分布式检索:让查询飞起来
单机无法处理海量向量的实时检索。Milvus 的分布式检索架构将计算任务并行化,实现了水平扩展。
4.1 查询节点与数据分布
- 一个集合的数据被水平划分为多个分区 。每个分区内的数据进一步被切分为多个段,分布在不同数据节点上持久化。
- 查询节点是无状态的。查询协调器根据负载情况,将不同的数据段(或分区)分配给不同的查询节点进行加载和管理。
- 当一个查询请求到来时:
- 查询协调器根据查询条件(如过滤表达式)确定需要搜索哪些分区和段。
- 协调器将搜索任务分发给管理着这些段的查询节点。
- 每个查询节点并行地在自己的内存/缓存中,对负责的段执行检索(包括标量过滤和向量相似度计算)。
- 各节点将本地 Top-K 结果返回给协调器或代理节点,进行归并排序,得到全局的 Top-K 结果后返回给客户端。
对象存储 查询节点3 查询节点2 查询节点1 查询协调器 代理服务 客户端 对象存储 查询节点3 查询节点2 查询节点1 查询协调器 代理服务 客户端 #mermaid-svg-QYWTjgEcPCbtc6Cd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QYWTjgEcPCbtc6Cd .error-icon{fill:#552222;}#mermaid-svg-QYWTjgEcPCbtc6Cd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QYWTjgEcPCbtc6Cd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QYWTjgEcPCbtc6Cd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QYWTjgEcPCbtc6Cd .marker.cross{stroke:#333333;}#mermaid-svg-QYWTjgEcPCbtc6Cd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QYWTjgEcPCbtc6Cd p{margin:0;}#mermaid-svg-QYWTjgEcPCbtc6Cd .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QYWTjgEcPCbtc6Cd text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QYWTjgEcPCbtc6Cd .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-QYWTjgEcPCbtc6Cd .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-QYWTjgEcPCbtc6Cd #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-QYWTjgEcPCbtc6Cd .sequenceNumber{fill:white;}#mermaid-svg-QYWTjgEcPCbtc6Cd #sequencenumber{fill:#333;}#mermaid-svg-QYWTjgEcPCbtc6Cd #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-QYWTjgEcPCbtc6Cd .messageText{fill:#333;stroke:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QYWTjgEcPCbtc6Cd .labelText,#mermaid-svg-QYWTjgEcPCbtc6Cd .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .loopText,#mermaid-svg-QYWTjgEcPCbtc6Cd .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QYWTjgEcPCbtc6Cd .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-QYWTjgEcPCbtc6Cd .noteText,#mermaid-svg-QYWTjgEcPCbtc6Cd .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-QYWTjgEcPCbtc6Cd .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QYWTjgEcPCbtc6Cd .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QYWTjgEcPCbtc6Cd .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QYWTjgEcPCbtc6Cd .actorPopupMenu{position:absolute;}#mermaid-svg-QYWTjgEcPCbtc6Cd .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-QYWTjgEcPCbtc6Cd .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QYWTjgEcPCbtc6Cd .actor-man circle,#mermaid-svg-QYWTjgEcPCbtc6Cd line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-QYWTjgEcPCbtc6Cd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} par并行查询 发起向量查询请求转发查询解析查询条件确定目标分区/段分配段A查询任务加载段A数据与索引执行本地检索(标量过滤+向量计算)返回本地Top-K结果分配段B查询任务加载段B数据与索引执行本地检索返回本地Top-K结果分配段C查询任务加载段C数据与索引执行本地检索返回本地Top-K结果归并排序所有结果返回全局Top-K结果返回最终查询结果
图:分布式查询并行执行流程
4.2 混合查询:向量 + 标量过滤
现实中的查询很少是纯向量搜索。Milvus 支持强大的混合查询:
python
# 示例:查找与某图片向量相似,且发布时间在2023年之后,分类为"动物"的条目
results = collection.search(
data=[query_vector],
anns_field="image_vector",
param={"metric_type": "L2", "params": {"nprobe": 10}},
limit=10,
expr='publish_year > 2023 and category == "animal"'
)
- 执行流程:查询节点会先利用倒排索引(如果标量字段已建索引)或暴力扫描进行标量过滤,得到一个候选向量集,然后仅对这个子集进行向量相似度计算。这极大地减少了不必要的向量距离计算。
4.3 一致性级别
Milvus 提供多种查询一致性级别,平衡性能与数据新鲜度:
- 强一致性:确保读到最新写入的数据,延迟最高。
- 会话一致性(默认):保证同一客户端会话内读到自身已提交的写入。
- 有界一致性:允许读到几秒钟前的数据状态,延迟最低,吞吐最高。
- 最终一致性:不保证顺序,只保证最终能读到。
5. 流批一体与数据生态
现代 AI 应用需要同时处理实时流数据和历史批数据。Milvus 的架构原生支持流批一体。
- 流处理:通过消息队列,新插入的数据能近乎实时地被查询节点感知和加载,支持低延迟的实时检索。
- 批处理:历史数据以段的形式存储在对象存储中,支持大规模的历史数据分析与检索。
- 统一入口:用户无需区分流和批,使用同一套 API 即可查询全量数据。
此外,Milvus 积极融入大数据生态:
- 与 Kafka/Pulsar 集成:直接消费业务日志流,实现向量数据的实时导入。
- 与 Spark/Flink 集成:方便进行大规模的离线向量索引构建和批量数据导入。
- 作为 LlamaIndex / LangChain 的向量存储:成为大模型应用(RAG)的核心记忆体。
总结与展望
Milvus 的架构设计是工程智慧的集中体现。它通过存储计算分离 获得了弹性,通过日志即数据 获得了可靠性,通过丰富的向量索引 获得了效率,通过分布式检索 获得了规模,通过流批一体获得了灵活性。
这种设计使得 Milvus 能够支撑从千万级到百亿级向量的 AI 应用场景,涵盖推荐系统、图像检索、智能问答、药物发现、欺诈检测等众多领域。随着 AI 模型多模态化和应用复杂化,对向量数据库的实时性、规模性和易用性提出了更高要求。Milvus 社区正在持续演进,例如探索更智能的索引自动选择、更紧密的云原生集成、以及更强的 GPU 加速能力,以巩固其作为 AI 原生数据基础设施核心的地位。
对于架构师和开发者而言,理解 Milvus 的内部机制,不仅能更好地使用它,更能从中汲取设计分布式数据系统的宝贵经验。