【深度好文】4、Milvus 存储设计深度解析

引言

作为一款主流的云原生向量数据库,Milvus 通过其独特的存储架构设计来保证高效的查询性能。本文将深入剖析 Milvus 的核心存储机制,特别是其最小存储单元 Segment 的完整生命周期,包括数据写入、持久化、合并以及索引构建等关键环节。

1. 数据写入流程

Milvus 中的每个 Collections 指定若干分片,每个分片对应一个虚拟通道(vchannel)。如下图所示,Milvus 会为日志代理中的每个 vchannel 分配一个物理通道(pchannel)。任何传入的插入/删除请求都会根据主键的哈希值路由到分片。

1.1 Insert 请求处理

当 Milvus 启动后,系统会在消息队列(Pulsar/Kafka)中创建数据接收管道,具体数量由 milvus.yaml 中的 rootCoord.dmlChannelNum 配置决定。数据写入流程如下:

  1. 请求接收与分片

    • Proxy 接收 insert 请求
    • 根据数据的 primary key 计算 hash 值
    • 将 hash 值对 collection 的 shards_num 取模,确定数据分片
  2. 数据传输机制

    • 每个 shard 的数据通过指定管道传输 ,多个 shard 可共享同一管道实现负载均衡
    • Data node 订阅对应 shard 数据 , 并尽可能把不同的 shard 分配到多个 data node 上。如果只有一个 data node ,那么就订阅所有的 shard 数据。
  3. Growing Segment 处理

    • 每个 partition 中的每个 shard 对应一个 growing segment
    • Data node 为每个 growing segment 维护缓存
    • 缓存用于存储未落盘的数据

下图展示了在 shards_num=2 的情况下,数据经由管道传输至 data node 的流程示意图:

2. 数据持久化机制

2.1 Growing Segment 持久化

Growing segment 的数据持久化受以下参数控制:

  • dataNode.segment.insertBufSize: 缓存最大容量(默认16MB)
  • dataNode.segment.syncPeriod: 数据最长停留时间(默认600秒)

当满足以下任一条件时,数据会被写入 S3/MinIO:

  1. 缓存数据量超过 insertBufSize
  2. 数据在缓存中停留时间超过 syncPeriod

为什这么设计:

1、减少 data node 内存占用,避免growing segment 内存占用过多。

2、如果系统发生故障之后 ,无需从mq 中重新拉取growing segment 的全部数据,已持久化的数据直接从S3/MinIO读取。

2.2 Sealed Segment 转换

Growing segment 在达到特定条件后会转换为 sealed segment:

  • 触发条件:数据量达到 dataCoord.segment.maxSize(默认1024MB) × dataCoord.segment.sealProportion(默认0.12)
  • 转换后会创建新的 growing segment 继续接收数据。

数据在 S3/MinIO 中的存储路径由 milvus.yaml 中 minio.bucketName(默认值 a-bucket)以及 minio.rootPath(默认值 files)共同决定。segment 数据的完整路径格式为:

minio.bucketName\]/\[minio.rootPath\]/insert_log/\[collection ID\]/\[partition ID\]/\[segment ID

在 docker 里面的显示如下:

3. 性能优化建议

3.1 避免频繁调用 flush()

  • 频繁调用 flush() 可能导致大量碎片化的小 segment
  • 建议依赖系统自动落盘机制
  • 仅在需要确认全量数据落盘时手动调用

3.2 Segment 管理优化

  1. 监控 Segment 数量

    • 使用 attu 工具监控 segment 分布
    • 适当调整 segment_size 参数
    • 根据数据量设置合理的 shards_num:
      • 百万级数据:shards_num=1
      • 千万级数据:shards_num=2
      • 亿级数据:shards_num=4或8
  2. Partition 规划

    • 合理控制 partition 数量
    • 避免过多 partition 影响系统性能

3.3 Clustering Key 设计

  • 针对范围查询场景优化
  • 优化批量数据删除性能
  • 提升 compaction 效率

3.4 Segment 合并与 Compaction

在持续执行 insert 请求时,sealed segment 的数量会随着新数据不断写入而增加。如果数据被拆分成过多小尺寸(如小于 100MB)的 segment,会影响系统的数据管理和查询效率。为此,data node 会通过 compaction 将若干较小的 sealed segment 合并成更大的 sealed segment。理想情况下,合并后 segment 大小会尽量接近 dataCoord.segment.maxSize(默认 1GB)。

Compaction 主要包括以下三种场景:

(1) 小文件合并(系统自动)
  • 触发条件:存在多个体积较小的 sealed segment,其总大小接近 1GB。
  • 优化效果:减少元数据开销,提高批量查询的性能。
(2) 删除数据清理(系统自动)
  • 触发条件:segment 中的被删除数据占比 ≥ dataCoord.compaction.single.ratio.threshold(默认 20%)。
  • 优化效果:释放存储空间,减少无效数据的重复扫描。
(3) 按聚类键(Clustering Key)重组(手动触发)
  • 使用场景:面向特定查询模式(如地域或时间范围检索)优化数据分布。
  • 调用方式:通过 SDK 调用 compaction,并按照指定的 Clustering Key 对 Segment 进行重组。

4. 索引构建机制

4.1 索引构建流程

索引构建由专门的 index node 负责执行。为了避免频繁的数据更新导致重复建索引,Milvus 采用了基于 segment 的索引构建策略:

4.1 临时索引 vs 持久化索引

对于每个 growing segment,query node 会在内存中为其建立临时索引,这些临时索引并不会持久化。

同理,当 query node 加载未建立索引的 sealed segment 时,也会创建临时索引。

关于临时索引的相关配置,可在 milvus.yaml 中通过 queryNode.segcore.interimIndex 进行调整。

当 data coordinator 监测到新的 sealed segment 生成后,会指示 index node 为其构建并持久化索引。然而,如果该 sealed segment 的数据量小于 indexCoord.segment.minSegmentNumRowsToEnableIndex (默认 1024 行) ,index node 将不会为其创建索引。

所有索引数据都被保存在以下路径:

minio.bucketName\]/\[minio.rootPath\]/index_files

5. 数据查询处理

5.1 查询执行流程

Milvus 支持两种主要的查询模式:

  • K 近邻搜索:返回与目标向量最接近的 K 个向量
  • 范围搜索:返回与目标向量距离在指定范围内的所有向量

查询流程如下:

  1. 查询请求广播

    • 搜索请求广播至所有 query node
    • 各节点并发执行搜索
    • 对本地 segment 进行剪枝和搜索
    • 汇总并返回结果
  2. 查询节点职责

    • 按照 query coord 指令加载或释放 segment
    • 在本地 segment 中执行搜索
    • Query node 之间相互独立
    • Proxy 负责归并各节点结果

5.2 数据一致性管理

Milvus 通过 handoff 机制管理数据一致性:

  1. Segment 状态转换

    • Growing segment:用于增量数据
    • Sealed segment:用于历史数据
    • Query node 通过订阅 vchannel 接收最新更新
  2. Handoff 流程

    • Growing segment 达到阈值后被 data coord 封存
    • 开始构建索引
    • Query coord 触发 handoff 操作 ,只有等新的 segment 创建之后,索引也可以使用了。旧的数据才从内存中进行删除。
    • 增量数据转换为历史数据
  3. 负载均衡

    • Query coord 根据多个指标分配 sealed segment:
      • 内存使用情况
      • CPU 负载
      • Segment 数量

总结

通过深入理解 Milvus 的存储设计,特别是 Segment 的生命周期管理,开发者可以更好地:

  1. 设计合理的数据写入策略
  2. 优化系统配置参数
  3. 确保系统在复杂场景下保持稳定的高性能表现

本文详细介绍了 Milvus 存储架构的核心组件和关键概念,希望能帮助开发者更好地使用和优化 Milvus 系统。

相关推荐
啦哈哈啦9 小时前
springboot + langchain4j + xinference + milvus
java·spring boot·语言模型·milvus
码观天工9 小时前
.NET 原生驾驭 AI 新基建实战系列(五):Milvus ── 大规模 AI 应用的向量数据库首选
c#·.net·milvus·向量数据库·高性能
游王子1 天前
Milvus(7):Schema、主字段和自动识别
数据库·milvus
非ban必选1 天前
spring-ai使用Document存储至milvus的数据结构
前端·spring·milvus
游王子1 天前
Milvus(5):Collections 查看、修改、加载和释放、删除
milvus
风路丞7 天前
windows使用docker-desktop安装milvus和可视化工具attu
docker·容器·milvus
樽酒ﻬق13 天前
打造AI应用基础设施:Milvus向量数据库部署与运维
数据库·人工智能·milvus
helloworld工程师15 天前
Spring AI应用:利用DeepSeek+嵌入模型+Milvus向量数据库实现检索增强生成--RAG应用(超详细)
人工智能·spring·milvus