当前位置: 首页 > 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 系统。

http://www.xdnf.cn/news/172855.html

相关文章:

  • 公网域名如何解析到内网ip服务器?自己域名映射外网访问
  • 3. 使用idea将一个git分支的部分提交记录合并到另一个git分支
  • Golang | 集合求交
  • 常用的性能提升手段--提纲
  • 二叉树的前序、中序和后序遍历:详解与实现
  • 非计算机专业如何利用AI开展跨学科和交叉研究
  • 智能硬件行业售后服务管理:提升客户体验的关键所在
  • Java:网络编程
  • CesiumEarth更新至1.14.0版本,重新设计了图层设置页面,优化了许多界面交互问题
  • K8S Pod 常见数据存储方案
  • Lua 第12部分 日期和时间
  • PH热榜 | 2025-04-27
  • HTML倒数
  • java 类的实例化过程,其中的相关顺序 包括有继承的子类等复杂情况,静态成员变量的初始化顺序,这其中jvm在干什么
  • xe-upload上传文件插件
  • WPF常用技巧汇总 - Part 2
  • Qt项目全局设置UTF-8编码方法(MSVS编译中文报错解决办法)
  • 新能源汽车运动控制器核心芯片选型与优化:MCU、DCDC与CANFD协同设计
  • 设计一个新能源汽车控制系统开发框架,并提供一个符合ISO 26262标准的模块化设计方案。
  • Java高频常用工具包汇总
  • [特殊字符]实战:使用 Canal + MQ + ES + Redis + XXL-Job 打造高性能地理抢单系统
  • Spark Mllib 机器学习
  • 第二章,网络类型及数据链路层协议
  • SMART:大模型在关键推理步骤辅导小模型,在保持高推理效率的同时,显著提升小模型的推理能力!!
  • python合并一个word段落中的run
  • 决策树相关案例
  • 【Node.js 】在Windows 下搭建适配 DPlayer 的轻量(简陋)级弹幕后端服务
  • Linux系统之设置开机启动运行桌面环境
  • 力扣hot100_子串_python版本
  • Nginx配置文件介绍