当前位置: 首页 > news >正文

【深度好文】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 系统。

相关文章:

  • 【Nginx】负载均衡配置详解
  • 【2025最新Java面试八股】如何在Spring启动过程中做缓存预热?
  • kafka 中消费者 groupId 是什么
  • [python] 基于WatchDog库实现文件系统监控
  • Seaborn模块练习题
  • GCC 内建函数汇编展开详解
  • 【数据挖掘】时间序列预测-时间序列预测策略
  • 脏读、幻读、可重复读
  • 反序列化漏洞2
  • 数据结构(七)---链式栈
  • 力扣HOT100之链表:23. 合并 K 个升序链表
  • ComfyUI for Windwos与 Stable Diffusion WebUI 模型共享修复
  • JavaScript 中 undefined 和 not defined 的区别
  • 【深度学习】多头注意力机制的实现|pytorch
  • 生物医学AI的特种算力需求:冷冻电镜数据处理中的GPU加速方案
  • GIS开发笔记(16)解决基于osg和osgearth三维地图上添加placeNode图标点击不易拾取的问题
  • UML 活动图详解之小轿车启动活动图分析
  • Dev控件RadioGroup 如何设置一排有N个显示或分为几行
  • 在Linux中,使用IO标准库进行读写操作。
  • 塔能合作模式:解锁工厂能耗精准节能新路径
  • 中国黄金协会:一季度我国黄金产量同比增1.49%,黄金消费量同比降5.96%
  • 广州海关原党委委员、副关长刘小威被开除党籍
  • 加拿大驾车撞人事件遇难人数升到11人
  • 独家丨申万宏源研究所将迎来新所长:首席策略分析师王胜升任
  • 又一名90后干部被查,已有多人倒在乡镇领导岗位上
  • 演员孙俪:中年人没有脆弱的时间,学习胡曼黎不内耗