← 返回文章列表

Milvus底层原理(一):概述与架构设计

2026-03-10·9 分钟阅读

前言

随着大语言模型(LLM)的爆发式发展,向量数据库作为 AI 应用基础设施的核心组件,正在经历前所未有的关注。从 RAG(检索增强生成)到推荐系统,从语义搜索到异常检测,向量数据库支撑着越来越多的 AI 应用场景。

Milvus 作为全球最流行的开源向量数据库之一,以其高性能、云原生、可扩展的架构设计,被众多企业用于生产环境。本系列将从底层源码角度深入剖析 Milvus 的实现原理,帮助读者不仅"知其然",更"知其所以然"。本文作为系列开篇,将从宏观视角介绍 Milvus 的整体架构设计。

技术亮点

技术点难度面试价值本文覆盖
向量相似度搜索原理⭐⭐⭐高频考点
存储计算分离架构⭐⭐⭐⭐架构设计
Milvus 分布式组件⭐⭐⭐进阶考点
向量索引分类⭐⭐⭐高频考点
Segment 数据模型⭐⭐⭐⭐源码级

面试考点

  1. 什么是向量数据库?与传统数据库有什么区别?
  2. Milvus 的架构是如何实现存储计算分离的?
  3. Milvus 有哪些核心组件?各自的作用是什么?
  4. 向量索引有哪些类型?各自的适用场景是什么?
  5. Milvus 如何实现分布式查询?

一、向量数据库概述

1.1 什么是向量数据库

向量数据库是一种专门用于存储、索引和查询高维向量数据的数据库系统。它能够高效地进行向量相似度搜索,支持大规模向量数据的存储和检索。

┌─────────────────────────────────────────────────────────────┐
│                    向量数据库核心定位                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  语义搜索    │  │  推荐系统   │  │  RAG 应用   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  图像检索    │  │  异常检测   │  │  问答系统   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 向量数据的特点

向量数据是将非结构化数据(文本、图像、音频等)通过 Embedding 模型转换得到的高维数值表示:

# 文本转向量示例
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

# 将文本转换为 384 维向量
texts = ["Milvus 是一个向量数据库", "向量搜索在 AI 中很重要"]
vectors = model.encode(texts)

print(vectors.shape)  # (2, 384)
print(vectors[0][:5])  # [0.123, -0.456, 0.789, ...]

向量数据的关键特征:

特征说明
高维度通常 128-2048 维,甚至更高
稠密/稀疏稠密向量(所有维度有值)或稀疏向量(大部分为 0)
语义相似性相似内容的向量在空间中距离较近
计算密集相似度计算需要大量浮点运算

1.3 相似度度量方法

向量数据库的核心能力是相似度搜索,常见的度量方法包括:

┌─────────────────────────────────────────────────────────────┐
│                    相似度度量方法                             │
├───────────────┬─────────────────────────────────────────────┤
│ 度量方法      │ 公式与说明                                   │
├───────────────┼─────────────────────────────────────────────┤
│ L2 距离       │ d = √Σ(xᵢ - yᵢ)²                           │
│ (欧氏距离)    │ 值越小越相似,适用于物理距离相关的场景        │
├───────────────┼─────────────────────────────────────────────┤
│ IP (内积)     │ s = Σxᵢyᵢ                                  │
│ (Inner Product)│ 值越大越相似,适用于已归一化的向量          │
├───────────────┼─────────────────────────────────────────────┤
│ COSINE        │ s = (x·y) / (‖x‖‖y‖)                       │
│ (余弦相似度)  │ 值越大越相似,适用于文本语义相似度           │
└───────────────┴─────────────────────────────────────────────┘
import numpy as np

def cosine_similarity(a, b):
    """余弦相似度计算"""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def l2_distance(a, b):
    """L2 距离计算"""
    return np.linalg.norm(a - b)

# 示例
v1 = np.array([1.0, 2.0, 3.0])
v2 = np.array([1.5, 2.5, 3.5])

print(f"余弦相似度: {cosine_similarity(v1, v2):.4f}")  # 0.9979
print(f"L2 距离: {l2_distance(v1, v2):.4f}")          # 0.8660

1.4 向量数据库 vs 传统数据库

┌─────────────────────────────────────────────────────────────────────┐
│                    向量数据库 vs 传统数据库                          │
├─────────────────┬─────────────────────┬─────────────────────────────┤
│ 特性            │ 传统数据库           │ 向量数据库                  │
├─────────────────┼─────────────────────┼─────────────────────────────┤
│ 数据类型        │ 结构化数据           │ 向量 + 标量                 │
│ 查询方式        │ 精确匹配             │ 近似最近邻(ANN)           │
│ 索引结构        │ B+ Tree、Hash        │ HNSW、IVF、DiskANN          │
│ 查询结果        │ 精确结果             │ Top-K 相似结果              │
│ 典型场景        │ 事务处理、报表       │ 语义搜索、推荐              │
│ 代表产品        │ MySQL、PostgreSQL    │ Milvus、Pinecone            │
└─────────────────┴─────────────────────┴─────────────────────────────┘

二、Milvus 整体架构

2.1 架构分层

Milvus 采用云原生的存储计算分离架构,从上到下分为四层:

┌─────────────────────────────────────────────────────────────────────┐
│                          接入层 (Access Layer)                       │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │                         Proxy                                 │  │
│  │     • 请求路由    • 结果归并    • 权限校验    • 限流控制       │  │
│  └──────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         协调服务层 (Coordinator Layer)               │
│  ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐       │
│  │ Root Coord │ │ Query Coord│ │ Data Coord │ │ Index Coord│       │
│  │  元数据管理 │ │  查询调度  │ │  数据管理  │ │  索引管理  │       │
│  └────────────┘ └────────────┘ └────────────┘ └────────────┘       │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         执行层 (Worker Layer)                        │
│  ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐       │
│  │ Query Node │ │ Data Node  │ │ Index Node │ │  流式节点  │       │
│  │  向量查询  │ │  数据写入  │ │  索引构建  │ │  流处理    │       │
│  └────────────┘ └────────────┘ └────────────┘ └────────────┘       │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         存储层 (Storage Layer)                       │
│  ┌────────────────────┐  ┌────────────────────┐  ┌──────────────┐  │
│  │     Meta Store     │  │     Log Broker    │  │  Object Store │  │
│  │   (etcd/MySQL)     │  │   (Pulsar/Kafka)  │  │  (MinIO/S3)   │  │
│  │     元数据存储      │  │      日志流       │  │   向量数据    │  │
│  └────────────────────┘  └────────────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────────────────┘

2.2 存储计算分离的优势

┌─────────────────────────────────────────────────────────────────────┐
│                    存储计算分离的优势                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 1. 独立扩展                                                  │   │
│  │    • 计算节点可根据查询负载独立扩缩容                        │   │
│  │    • 存储节点可根据数据量独立扩容                            │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 2. 成本优化                                                  │   │
│  │    • 计算节点使用高性能 SSD                                  │   │
│  │    • 存储节点使用廉价对象存储                                │   │
│  │    • 无状态计算节点可随时释放                                │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 3. 高可用                                                    │   │
│  │    • 存储层多副本保证数据安全                                │   │
│  │    • 计算节点故障可快速恢复                                  │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.3 核心组件详解

2.3.1 Proxy(代理层)

Proxy 是 Milvus 的接入层,负责处理客户端请求:

// pkg/proxy/proxy.go - Proxy 核心职责

type Proxy struct {
    // 请求路由
    func (p *Proxy) Search(ctx context.Context, req *SearchRequest) {
        // 1. 解析请求,确定目标 Shard
        // 2. 将请求分发到多个 QueryNode
        // 3. 收集并归并结果
        // 4. 返回 Top-K 结果
    }
    
    // 结果归并
    func (p *Proxy) reduceSearchResults(results []*SearchResult) {
        // 对多个 QueryNode 的结果进行归并排序
        // 返回全局 Top-K
    }
}

Proxy 的主要职责:

功能说明
请求解析解析 gRPC 请求,验证参数合法性
路由分发根据分区/分片信息将请求路由到正确的节点
负载均衡在多个副本间进行负载均衡
结果归并将多个节点的查询结果归并排序
限流控制请求速率限制,防止系统过载

2.3.2 Root Coordinator(根协调器)

Root Coordinator 负责全局元数据管理:

// pkg/rootcoord/root_coord.go

type RootCoordinator struct {
    // 元数据操作
    func (c *RootCoordinator) CreateCollection(ctx, req) {
        // 1. 分配 Collection ID
        // 2. 创建 Collection Schema
        // 3. 分配 Partition ID
        // 4. 通知 DataCoord 创建 Segment
        // 5. 持久化元数据到 etcd
    }
    
    // 分片管理
    func (c *RootCoordinator) AllocID(ctx) (int64, error) {
        // 分配全局唯一 ID(雪花算法)
    }
}

Root Coordinator 管理的元数据:

  • Collection(集合)Schema
  • Partition(分区)信息
  • Segment(数据段)元数据
  • 字段索引信息
  • 全局时间戳分配

2.3.3 Query Coordinator(查询协调器)

Query Coordinator 负责查询调度和负载均衡:

// pkg/querycoord/query_coord.go

type QueryCoordinator struct {
    // 查询节点管理
    func (c *QueryCoordinator) LoadCollection(ctx, req) {
        // 1. 获取 Collection 的所有 Segment
        // 2. 将 Segment 分配到 QueryNode
        // 3. 监控加载进度
        // 4. 更新路由表
    }
    
    // 负载均衡
    func (c *QueryCoordinator) BalanceSegments() {
        // 定期检查 QueryNode 负载
        // 迁移 Segment 实现均衡
    }
}

Query Coordinator 的核心职责:

  • QueryNode 注册与心跳管理
  • Segment 到 QueryNode 的分配
  • 查询负载均衡
  • 副本管理

2.3.4 Data Coordinator(数据协调器)

Data Coordinator 负责数据写入和 Segment 管理:

// pkg/datacoord/data_coord.go

type DataCoordinator struct {
    // Segment 管理
    func (c *DataCoordinator) AllocSegment(ctx, req) {
        // 1. 选择合适的 Channel
        // 2. 分配新的 Segment ID
        // 3. 记录 Segment 元数据
    }
    
    // 数据 Flush
    func (c *DataCoordinator) Flush(ctx, req) {
        // 1. 通知 DataNode 执行 Flush
        // 2. 更新 Segment 状态为 Flushed
        // 3. 触发索引构建
    }
}

2.3.5 Index Coordinator(索引协调器)

Index Coordinator 负责索引构建任务调度:

// pkg/indexcoord/index_coord.go

type IndexCoordinator struct {
    // 索引任务调度
    func (c *IndexCoordinator) CreateIndex(ctx, req) {
        // 1. 创建索引元数据
        // 2. 为每个 Segment 创建索引任务
        // 3. 分配任务到 IndexNode
        // 4. 监控构建进度
    }
}

2.4 Worker 节点详解

2.4.1 Query Node(查询节点)

Query Node 是查询的执行单元,负责向量搜索:

// pkg/querynode/query_node.go

type QueryNode struct {
    // 向量搜索
    func (n *QueryNode) Search(ctx, req) *SearchResult {
        // 1. 从 Segment 加载向量数据
        // 2. 执行索引搜索或暴力搜索
        // 3. 应用标量过滤条件
        // 4. 返回 Top-K 结果
    }
    
    // 流式数据消费
    func (n *QueryNode) ConsumeInsertData() {
        // 从 DML Channel 消费增量数据
        // 写入 growing segment
    }
}

Query Node 的数据管理:

┌─────────────────────────────────────────────────────────────────┐
│                    Query Node 数据结构                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    Historical Data                       │   │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐                  │   │
│  │  │Segment 1│  │Segment 2│  │Segment 3│  ... (Sealed)    │   │
│  │  │ 索引加载 │  │ 索引加载 │  │ 索引加载 │                  │   │
│  │  └─────────┘  └─────────┘  └─────────┘                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    Streaming Data                        │   │
│  │  ┌─────────────────────────────────────────────────┐    │   │
│  │  │              Growing Segment                      │    │   │
│  │  │  实时接收新数据,暴力搜索                          │    │   │
│  │  └─────────────────────────────────────────────────┘    │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.4.2 Data Node(数据节点)

Data Node 负责数据写入和持久化:

// pkg/datanode/data_node.go

type DataNode struct {
    // 数据消费与写入
    func (n *DataNode) ConsumeInsertMsg(msg *InsertMsg) {
        // 1. 从 DML Channel 消费写入消息
        // 2. 写入 Growing Segment
        // 3. 当 Segment 满时触发 Flush
    }
    
    // 数据 Flush
    func (n *DataNode) FlushSegment(segmentID int64) {
        // 1. 将内存数据序列化
        // 2. 写入对象存储(MinIO/S3)
        // 3. 通知 DataCoord 更新元数据
    }
}

2.4.3 Index Node(索引节点)

Index Node 负责向量索引的构建:

// pkg/indexnode/index_node.go

type IndexNode struct {
    // 索引构建
    func (n *IndexNode) CreateIndex(ctx, req) {
        // 1. 从对象存储加载原始向量
        // 2. 根据索引类型构建索引
        //    - HNSW: 构建图结构
        //    - IVF: 聚类 + 倒排列表
        //    - DiskANN: Vamana 图算法
        // 3. 序列化索引文件
        // 4. 上传到对象存储
    }
}

三、数据模型

3.1 Collection(集合)

Collection 是 Milvus 中的顶层逻辑容器,类似于关系数据库中的表:

┌─────────────────────────────────────────────────────────────────┐
│                    Collection 结构                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Collection: product_embeddings                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ Schema:                                                  │   │
│  │  ┌──────────────┬──────────────┬──────────────┐         │   │
│  │  │ Field Name   │ Field Type   │ Description  │         │   │
│  │  ├──────────────┼──────────────┼──────────────┤         │   │
│  │  │ id           │ Int64        │ Primary Key  │         │   │
│  │  │ title        │ VarChar      │ 商品标题     │         │   │
│  │  │ price        │ Float        │ 价格         │         │   │
│  │  │ embedding    │ FloatVector  │ 768 维向量   │         │   │
│  │  └──────────────┴──────────────┴──────────────┘         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
# 创建 Collection 示例
from pymilvus import Collection, FieldSchema, CollectionSchema, DataType

fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
    FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=256),
    FieldSchema(name="price", dtype=DataType.FLOAT),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
]

schema = CollectionSchema(fields=fields, description="商品向量")
collection = Collection(name="product_embeddings", schema=schema)

3.2 Partition(分区)

Partition 是 Collection 的逻辑分区,用于加速查询:

┌─────────────────────────────────────────────────────────────────┐
│                    Partition 结构                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Collection: product_embeddings                                 │
│  ├── Partition: electronics (电子产品)                          │
│  │   ├── Segment 1                                             │
│  │   ├── Segment 2                                             │
│  │   └── ...                                                   │
│  ├── Partition: clothing (服装)                                │
│  │   ├── Segment 3                                             │
│  │   └── ...                                                   │
│  └── Partition: books (图书)                                   │
│      └── Segment 4                                             │
│                                                                 │
│  查询时指定 Partition 可跳过无关数据,提升性能                   │
└─────────────────────────────────────────────────────────────────┘

3.3 Segment(数据段)

Segment 是 Milvus 数据存储的最小单元,分为两种状态:

┌─────────────────────────────────────────────────────────────────┐
│                    Segment 生命周期                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    达到大小阈值     ┌─────────────┐            │
│  │   Growing   │ ──────────────────► │   Sealed    │            │
│  │  (增长中)   │                     │  (已封存)   │            │
│  │             │                     │             │            │
│  │ • 内存存储  │                     │ • 对象存储  │            │
│  │ • 暴力搜索  │                     │ • 索引搜索  │            │
│  │ • 实时写入  │                     │ • 只读      │            │
│  └─────────────┘                     └─────────────┘            │
│                                             │                   │
│                                   Flush 完成│                   │
│                                             ▼                   │
│                                      ┌─────────────┐            │
│                                      │  Flushed    │            │
│                                      │  (已持久化) │            │
│                                      │             │            │
│                                      │ • 索引构建  │            │
│                                      │ • 可被加载  │            │
│                                      └─────────────┘            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Segment 的内部结构:

┌─────────────────────────────────────────────────────────────────┐
│                    Segment 内部结构                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Segment = 多个 Column(列式存储)                              │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Column: id (Int64)                                       │  │
│  │ [1, 2, 3, 4, 5, ...]                                     │  │
│  └──────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Column: title (VarChar)                                  │  │
│  │ ["手机", "电脑", "耳机", ...]                             │  │
│  └──────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Column: embedding (FloatVector)                          │  │
│  │ [[0.1, 0.2, ...], [0.3, 0.4, ...], ...]                  │  │
│  └──────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ Index File (可选)                                         │  │
│  │ HNSW/IVF/DiskANN 索引文件                                 │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3.4 列式存储设计

Milvus 采用列式存储,针对向量数据优化:

// pkg/storage/column.go

type Column interface {
    Name() string
    Type() FieldType
    Len() int
    Get(i int) interface{}
}

// 向量列
type VectorColumn struct {
    name     string
    dim      int
    data     []float32  // 连续内存存储
}

func (c *VectorColumn) Get(i int) []float32 {
    start := i * c.dim
    return c.data[start : start+c.dim]
}

列式存储的优势:

优势说明
压缩效率高相同类型数据连续存储,压缩率高
查询效率高只读取需要的列,减少 IO
向量化计算利用 SIMD 指令加速向量运算

四、向量索引概述

4.1 为什么需要索引

向量相似度搜索的核心挑战是高维空间中的"维度诅咒":

┌─────────────────────────────────────────────────────────────────┐
│                    向量搜索的挑战                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  暴力搜索时间复杂度: O(N × D)                                    │
│  • N: 向量数量(可达数十亿)                                    │
│  • D: 向量维度(通常 128-2048)                                 │
│                                                                 │
│  示例: 10 亿向量 × 768 维 = 7.68 亿次浮点运算/查询               │
│                                                                 │
│  解决方案: 近似最近邻搜索 (ANN - Approximate Nearest Neighbor)  │
│  • 牺牲少量精度换取数量级的速度提升                             │
│  • 索引空间复杂度: O(N) 或 O(N × log N)                         │
│  • 查询时间复杂度: O(log N) 或 O(√N)                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4.2 索引类型分类

Milvus 支持多种向量索引类型:

┌─────────────────────────────────────────────────────────────────┐
│                    向量索引分类                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    基于树的索引                          │   │
│  │  KD-Tree, Ball Tree, Annoy                              │   │
│  │  适用于低维数据,高维效果差                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    基于图的索引                          │   │
│  │  HNSW, NSW, DiskANN (Milvus 重点支持)                   │   │
│  │  高召回率、低延迟,内存消耗较大                           │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    基于聚类的索引                        │   │
│  │  IVF-Flat, IVF-PQ, IVF-SQ8 (Milvus 重点支持)            │   │
│  │  构建快、内存可控,需调参                                 │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    基于量化的索引                        │   │
│  │  PQ, SQ8, OPQ                                           │   │
│  │  大幅压缩内存,精度有损失                                 │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4.3 Milvus 支持的索引类型

索引类型适用度量特点适用场景
FLAT所有暴力搜索,精度最高小数据量、高精度要求
IVF_FLAT所有聚类分桶,平衡精度速度中等数据量
IVF_PQ所有聚类 + 量化,内存高效大数据量、内存受限
IVF_SQ8所有聚类 + 标量量化内存优化场景
HNSWL2/COSINE图索引,高性能高性能要求场景
DISKANNL2/COSINE磁盘索引,成本低超大规模数据
GPU_IVF_FLAT所有GPU 加速GPU 环境可用
GPU_IVF_PQ所有GPU 加速 + 量化GPU 环境、大数据量

4.4 索引选择指南

# 索引选择决策树
def choose_index(data_size: int, memory_limit: int, latency_requirement: float):
    """
    根据数据规模和需求选择合适的索引
    """
    if data_size < 100_000:
        # 小数据量,暴力搜索即可
        return "FLAT"
    
    if latency_requirement < 10:  # ms
        # 低延迟要求,选择图索引
        if memory_limit > data_size * 768 * 4 * 1.5:
            return "HNSW"  # 内存充足
        else:
            return "DISKANN"  # 使用磁盘索引
    
    if memory_limit < data_size * 768 * 4 * 0.3:
        # 内存严重不足,使用量化
        return "IVF_PQ"
    
    # 平衡选择
    return "IVF_FLAT"

五、查询执行流程

5.1 搜索请求处理流程

一个完整的向量搜索请求在 Milvus 中的处理流程:

┌─────────────────────────────────────────────────────────────────┐
│                    搜索请求处理流程                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 客户端发送 SearchRequest                                   │
│     │                                                           │
│     ▼                                                           │
│  2. Proxy 接收请求                                              │
│     │ • 参数校验                                                │
│     │ • 获取 Collection 元数据                                  │
│     │ • 确定目标 Partition 和 Segment                           │
│     ▼                                                           │
│  3. Proxy 分发请求到多个 QueryNode                              │
│     │ • 每个 QueryNode 负责部分 Segment                         │
│     │ • 并行执行搜索                                            │
│     ▼                                                           │
│  4. QueryNode 执行搜索                                          │
│     │ • 加载 Segment 数据/索引                                  │
│     │ • 执行向量搜索(索引搜索或暴力搜索)                       │
│     │ • 应用标量过滤条件                                        │
│     │ • 返回局部 Top-K 结果                                     │
│     ▼                                                           │
│  5. Proxy 归并结果                                              │
│     │ • 收集所有 QueryNode 结果                                 │
│     │ • 全局 Top-K 归并排序                                     │
│     │ • 返回最终结果                                            │
│     ▼                                                           │
│  6. 返回结果给客户端                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.2 代码示例:完整搜索流程

from pymilvus import connections, Collection

# 1. 连接 Milvus
connections.connect(host="localhost", port="19530")

# 2. 获取 Collection
collection = Collection("product_embeddings")

# 3. 加载 Collection 到内存(QueryNode)
collection.load()

# 4. 执行向量搜索
search_params = {
    "metric_type": "COSINE",
    "params": {"nprobe": 10}  # IVF 索引参数
}

results = collection.search(
    data=[query_vector],          # 查询向量
    anns_field="embedding",       # 向量字段
    param=search_params,          # 搜索参数
    limit=10,                     # Top-K
    expr="price < 1000",          # 标量过滤条件
    partition_names=["electronics"]  # 指定分区
)

# 5. 处理结果
for hits in results:
    for hit in hits:
        print(f"ID: {hit.id}, Distance: {hit.distance}")

5.3 结果归并算法

Proxy 需要将多个 QueryNode 的结果归并:

// pkg/proxy/impl/reduce.go

// 归并搜索结果(类似归并排序的 K 路归并)
func reduceSearchResults(results []*SearchResult, topK int) *SearchResult {
    // 使用最小堆进行 Top-K 归并
    heap := NewMinHeap()
    
    for _, result := range results {
        for _, item := range result.TopK {
            heap.Push(item)
            if heap.Len() > topK {
                heap.Pop()  // 移除最小的
            }
        }
    }
    
    // 提取最终 Top-K
    final := make([]*SearchResultItem, topK)
    for i := topK - 1; i >= 0; i-- {
        final[i] = heap.Pop()
    }
    
    return &SearchResult{TopK: final}
}

六、分布式设计

6.1 数据分片策略

Milvus 通过 Channel 实现数据分片:

┌─────────────────────────────────────────────────────────────────┐
│                    数据分片设计                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Collection 分片策略:                                           │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                   DML Channels                           │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐              │   │
│  │  │Channel 0 │  │Channel 1 │  │Channel 2 │  ...         │   │
│  │  │ (Shard 0)│  │ (Shard 1)│  │ (Shard 2)│              │   │
│  │  └────┬─────┘  └────┬─────┘  └────┬─────┘              │   │
│  │       │             │             │                     │   │
│  │       ▼             ▼             ▼                     │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐              │   │
│  │  │DataNode 0│  │DataNode 1│  │DataNode 2│              │   │
│  │  │ 消费写入 │  │ 消费写入 │  │ 消费写入 │              │   │
│  │  └──────────┘  └──────────┘  └──────────┘              │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  写入时根据主键 Hash 到对应 Channel                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.2 副本与高可用

Milvus 支持多副本机制:

┌─────────────────────────────────────────────────────────────────┐
│                    多副本架构                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                  QueryNode 副本组                        │   │
│  │                                                         │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │   │
│  │  │  Replica 0   │  │  Replica 1   │  │  Replica 2   │  │   │
│  │  │              │  │              │  │              │  │   │
│  │  │ QueryNode-A  │  │ QueryNode-B  │  │ QueryNode-C  │  │   │
│  │  │ Segment 1,2  │  │ Segment 1,2  │  │ Segment 1,2  │  │   │
│  │  └──────────────┘  └──────────────┘  └──────────────┘  │   │
│  │                                                         │   │
│  │  • 相同数据分布在多个副本                               │   │
│  │  • 查询负载均衡到不同副本                               │   │
│  │  • 单副本故障不影响服务                                 │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
# 创建多副本 Collection
from pymilvus import Collection

collection = Collection("product_embeddings")
collection.load(replica_number=3)  # 3 个副本

# 查看副本信息
replicas = collection.get_replicas()
print(f"副本数量: {len(replicas)}")

6.3 故障恢复机制

┌─────────────────────────────────────────────────────────────────┐
│                    故障恢复机制                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  QueryNode 故障恢复:                                           │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ 1. QueryCoord 通过心跳检测到节点故障                     │   │
│  │ 2. 将故障节点的 Segment 重新分配                         │   │
│  │ 3. 新节点从对象存储加载数据/索引                         │   │
│  │ 4. 更新路由表,恢复服务                                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  DataNode 故障恢复:                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ 1. DataCoord 检测到节点故障                              │   │
│  │ 2. 重新分配 Channel 订阅                                 │   │
│  │ 3. 新节点从 Log Broker 恢复未处理的消息                  │   │
│  │ 4. 继续处理写入请求                                      │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

七、与其他向量数据库对比

7.1 主流向量数据库对比

┌───────────────────────────────────────────────────────────────────────────┐
│                    主流向量数据库对比                                      │
├─────────────┬──────────────┬──────────────┬──────────────┬───────────────┤
│ 特性        │ Milvus       │ Pinecone     │ Weaviate     │ Chroma        │
├─────────────┼──────────────┼──────────────┼──────────────┼───────────────┤
│ 部署方式    │ 自托管/云    │ 仅云托管     │ 自托管/云    │ 本地/云       │
│ 开源        │ ✅ Apache 2.0│ ❌ 闭源      │ ✅ BSD       │ ✅ Apache 2.0 │
│ 分布式      │ ✅           │ ✅           │ ✅           │ ❌ 单机       │
│ 索引类型    │ 丰富         │ 有限         | 中等         │ 有限          │
│ 混合查询    │ ✅           │ ✅           │ ✅           │ 有限          │
│ 存储计算分离│ ✅           │ ✅           │ ❌           │ ❌            │
│ 水平扩展    │ ✅           │ ✅           │ ✅           │ ❌            │
│ 性能        │ ⭐⭐⭐⭐⭐       │ ⭐⭐⭐⭐        │ ⭐⭐⭐⭐        │ ⭐⭐⭐          │
│ 适用场景    │ 企业生产     │ 快速原型     │ 知识图谱     │ 开发测试     │
└─────────────┴──────────────┴──────────────┴──────────────┴───────────────┘

7.2 Milvus 的优势

  1. 开源生态:完全开源,社区活跃,文档完善
  2. 高性能:支持多种高性能索引,查询延迟低
  3. 可扩展:存储计算分离,支持水平扩展
  4. 功能丰富:支持标量过滤、混合查询、多模态
  5. 生产就绪:被众多企业验证,稳定性高

八、学习路线图

8.1 本系列文章结构

┌─────────────────────────────────────────────────────────────────┐
│                  Milvus 底层原理系列                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  第一章(本文)概述与架构设计                                   │
│        │                                                        │
│        ▼                                                        │
│  第二章 向量索引算法基础                                        │
│        │                                                        │
│        ▼                                                        │
│  第三章 IVF 索引家族                                            │
│        │                                                        │
│        ▼                                                        │
│  第四章 HNSW 图索引                                             │
│        │                                                        │
│        ▼                                                        │
│  第五章 DiskANN 磁盘索引                                        │
│        │                                                        │
│        ▼                                                        │
│  第六章 GPU 索引加速                                            │
│        │                                                        │
│        ▼                                                        │
│  第七章 数据模型与存储                                          │
│        │                                                        │
│        ▼                                                        │
│  第八章 数据写入流程                                            │
│        │                                                        │
│        ▼                                                        │
│  第九章 数据读取流程                                            │
│        │                                                        │
│        ▼                                                        │
│  第十章 分布式架构详解                                          │
│        │                                                        │
│        ▼                                                        │
│  第十一章 分片与路由策略                                        │
│        │                                                        │
│        ▼                                                        │
│  第十二章 副本与高可用                                          │
│        │                                                        │
│        ▼                                                        │
│  第十三章 事务与一致性                                          │
│        │                                                        │
│        ▼                                                        │
│  第十四章 内存与缓存管理                                        │
│        │                                                        │
│        ▼                                                        │
│  第十五章 生产环境实践                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

8.2 推荐学习资源

类型资源说明
官方milvus.io官方文档
源码github.com/milvus-io/milvus最新源码
论文HNSW, DiskANN索引算法论文
工具Attu, Birdwatcher管理工具

总结

本文从宏观视角介绍了 Milvus 向量数据库的整体架构设计,包括:

  1. 向量数据库基础:向量数据特点、相似度度量、与传统数据库的区别
  2. Milvus 架构:四层架构设计、存储计算分离、核心组件职责
  3. 数据模型:Collection、Partition、Segment 的设计
  4. 向量索引:索引分类、Milvus 支持的索引类型、选择指南
  5. 分布式设计:数据分片、多副本、故障恢复
  6. 对比分析:与其他向量数据库的对比

下一章将深入分析向量索引算法的基础知识,包括暴力搜索、向量量化、以及索引算法的理论基础。

参考资料

分享: