主题
Milvus 监控指标与告警
一句话:Milvus 的监控要沿着「接入 → 协调 → 查询 → 数据 → 依赖」一条链看,因为它的故障通常不是单点报错,而是某个依赖变慢后,沿段、索引、加载逐级放大成查询不可用。
适用版本:Milvus 2.5.x / 2.6.x。2.6 引入 MixCoord、StreamingNode 与 Woodpecker WAL,部分组件和指标来源随之变化;指标名以实际 /metrics 输出和官方 Grafana dashboard 为准。
为什么需要
Milvus 把计算和存储拆开:段文件在对象存储,元数据在 etcd,写入先过 WAL,查询节点加载索引后提供服务。这条链比单机向量库长得多,任何一环变慢都不会立刻报错,而是以「查询偶尔变慢」「collection 加载超时」「写入成功但查不到」的形式出现。只看进程存活或 /healthz,会错过整个恶化过程;必须有一条从写入到检索的端到端视角。
核心机制
指标分六层看
| 层 | 关键指标 | 它反映什么 |
|---|---|---|
| 接入 Proxy | search/query/insert QPS、P99 延迟、错误码与限流计数 | 入口是否过载,失败来自客户端还是下游 |
| 协调 | MixCoord(2.5 为多个独立 coord)存活与选举、任务队列积压、load/release 进度 | 元数据与调度是否卡住;协调层卡住时所有 DDL 都会超时 |
| 查询 QueryNode | 进程/容器内存、已加载 collection 与段数、sealed/growing 段数、search 延迟、cache 命中率 | 查询节点的内存与扇出是否健康 |
| 数据 DataNode/StreamingNode | flush 任务数与耗时、compaction 进度、导入任务、WAL 消费延迟 | 写入能否及时落段,growing 段能否转 sealed |
| 依赖 | etcd 有无 leader、leader 切换、WAL fsync 延迟、对象存储延迟与错误率、消息队列 backlog | 最容易被忽略、却最先恶化的一层 |
| 索引构建 | 构建任务进度、排队数、失败数 | 索引建不完,collection 就无法正常加载与查询 |
2.6 的 Woodpecker 把 WAL 内嵌后,消息积压要看 Woodpecker 自身指标,不再有 Kafka/Pulsar 的 consumer lag;具体指标名与默认值以官方文档为准。
指标怎么连成雪崩
这条链的起点常常不是 Milvus 自己,而是对象存储或 etcd。growing 段走暴力扫描,数量一多,查询延迟先涨;延迟带来重试,重试让 QueryNode 更忙;一旦 OOM,副本会重新分配,加载又要从对象存储读一遍,形成第二波压力。
五条典型恶化链路
- growing 段过多:小批量高频写入 → 段来不及 sealed → 查询同时扫索引段和增量段 → P99 上升。段数比数据总量更早反映问题。
- 索引构建积压:导入后集中建索引,或 IndexNode 不足 → 构建排队/失败 → collection 无法 load 或只能部分加载 → 查询报未加载。
- 对象存储变慢:flush、compaction、load 都要读对象存储 → 段无法及时 sealed、查询节点加载超时 → 写入可见性变差。
- QueryNode 内存耗尽:加载副本过多或查询中间结果过大 → OOM/换页 → 副本重分配 → 其余节点承接更多段,连锁过载。
- etcd 抖动:leader 频繁切换或 fsync 变慢 → 元数据操作超时 → collection 创建、load、DDL 卡住,表现为「集群像死了一样」。
资源指标之外还有两项检查:get_load_state 确认 collection 真的处于 Loaded,get_collection_stats 核对行数——写入成功不等于可查。
最小上手
单机版前提:Docker Compose 能起 etcd、MinIO 与 Milvus 三个容器,本地验证足够。
bash
curl -L -o docker-compose.yml \
https://github.com/milvus-io/milvus/releases/download/v2.6.0/milvus-standalone-docker-compose.yml
docker compose up -d
curl -s http://localhost:9091/healthz # 期望 OK
curl -s http://localhost:9091/metrics | head # Prometheus 文本格式集群里每个组件默认在 9091 暴露 /metrics;把各 Pod 抓进 Prometheus 后,导入官方仓库中与版本匹配的 Grafana dashboard,比自建面板更省事。
python
from pymilvus import MilvusClient
c = MilvusClient(uri="http://localhost:19530")
print(c.get_load_state("rag")) # 关注 state 是否为 Loaded
print(c.get_collection_stats("rag")) # 行数是否与预期一致关键 PromQL(指标名与 label 以实际 /metrics 为准,2.6 有重命名):
promql
# Proxy 查询 P99(直方图)
histogram_quantile(0.99,
sum by (le) (rate(milvus_proxy_search_latency_bucket[5m])))
# 查询错误率
sum(rate(milvus_proxy_search_requests_total{status="fail"}[5m]))
/ sum(rate(milvus_proxy_search_requests_total[5m]))
# growing 段数量,按 collection 看趋势
sum by (collection) (milvus_querynode_segment_num{segment_state="Growing"})
# etcd WAL fsync P99
histogram_quantile(0.99,
rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m]))对象存储与消息队列的指标来自 MinIO/S3 exporter 和 Kafka/Pulsar/Woodpecker:看错误率、TTFB、consumer lag 或 backlog 的趋势,而不是某一刻的绝对值。
生产动作
告警清单(8–10 条)
| 告警 | 起点(按基线校准) | 为什么是先兆 |
|---|---|---|
| 查询 P99 | 超过基线 2 倍持续 5 分钟 | 最早暴露 growing 段、对象存储或 GC 问题 |
| 查询错误率 | 5 分钟内 > 1% | 可能是未加载、超时或 Proxy 限流 |
| growing 段数 | 单 collection 持续增长 10 分钟 | 段无法 sealed,查询扇出与暴力扫描加重 |
| 索引构建失败/积压 | 任一失败,或排队持续 15 分钟 | 建不完就无法 load,查询直接不可用 |
| QueryNode 内存 | working set 超 limit 的 80% 持续 10 分钟 | 逼近 OOM;重启/副本重分配会带来第二波压力 |
| 对象存储错误率/TTFB | 错误率 > 1%,或 P99 明显偏离基线 | flush、compaction、load 同时受影响 |
| etcd 健康 | 无 leader、leader 频繁切换、fsync P99 超基线 | 元数据卡住会让所有协调操作超时 |
| WAL/消息积压 | backlog 持续增长 10 分钟 | 写入积压,数据可见性变慢 |
| collection 加载状态 | 预期 Loaded 的 collection 变为 NotLoad | 查询会直接失败,且常伴随协调层异常 |
处置顺序
- 先分读路径还是写路径。查询慢但 CPU 不高,优先查对象存储延迟、段数量和 compaction;写入积压则查 WAL、DataNode/StreamingNode 和对象存储。
- 查询变慢:先看 growing 段数与索引构建进度;入口限流、release 冷 collection 通常比立刻加 QueryNode 更快。加节点会触发段重新分配与加载,低峰做更稳。
- 索引积压:暂停新的批量导入,确认 IndexNode 资源与对象存储写入,再扩索引构建能力;不要在构建高峰重启协调层。
- QueryNode 内存告急:先 release 低优先级 collection、降低副本数或查询
limit,再扩节点;OOM 后的自动重启会重新拉取段,需预留带宽。 - 依赖异常与重启:etcd 抖动时避免 DDL 与大规模 load,等 leader 稳定;对象存储慢时降低写入与 compaction 并发。协调层或 QueryNode 重启会触发任务重放与段重载,通常不是第一手段。
长处与代价
Milvus 的长处是专用向量检索:索引类型多(HNSW、IVF、DiskANN、量化等)、计算存储分离带来的规模弹性、原生多租户与混合检索,适合数据量和 QPS 都上来的场景。代价是:
- 组件多、链路长,Proxy、协调、QueryNode、DataNode、etcd、对象存储、WAL 都要懂。
- 不是通用数据库,事务、join、强约束仍在业务库;它存的是可重建的检索副本。
- 强依赖对象存储与消息队列/WAL,依赖抖动会直接变成查询抖动;内存与运维成本高,小团队自建未必比托管便宜。
| 适用 | 不适用 |
|---|---|
| 千万到十亿级向量、较高 QPS | 百万级以内且已有 PostgreSQL 的业务 |
| 多租户、需要 partition key 隔离 | 需要事务、join、强约束的主数据存储 |
| 需要多种索引/量化/GPU 或混合检索 | 团队没有 K8s 与分布式存储运维能力 |
与 pgvector、ES 的取舍
| 维度 | pgvector | Elasticsearch | Milvus |
|---|---|---|---|
| 数据规模 | 单库能承载的量级,受 PostgreSQL 限制 | 中大规模型,向量增大会显著吃内存 | 大规模,计算与存储可分别扩 |
| 延迟特征 | 同库查询、链路短,受索引与过滤影响 | 分片扇出 + 归并,过滤聚合成熟 | 多一跳网络与段加载,冷查询更明显 |
| 运维成本 | 最低,复用现有数据库 | 中高,需懂分片、堆与水位 | 最高,组件与依赖最多 |
| 适合的前提 | 已有 PG、要事务一致、规模不大 | 已有 ES、关键词与向量混合 | 规模、QPS、多租户至少有一项明显吃紧 |
边界
- 监控面板全绿不等于检索正确:召回质量、embedding 版本、删除是否生效,都不在资源指标里。
- 指标名与 dashboard 强绑定版本,升级 2.6 后要重新核对;告警阈值必须按基线校准,不同索引类型、
ef、过滤强度下同一 P99 的含义不同。 - Milvus 存的是可重建副本,监控之外仍要备份 etcd 元数据与对象存储文件,并做恢复演练。
- 上面的对比不回答「谁更强」:数据量、延迟要求、团队能力任一变化,结论都可能反转,最终以自己数据分布下的压测为准。
讲清楚
Milvus 的监控要沿一条链看:Proxy 的 QPS、P99 和错误率,协调层的任务积压,QueryNode 的内存与段状态,DataNode 的 flush 与 compaction,再往下是 etcd、对象存储和 WAL。它最常见的雪崩不是某个组件挂了,而是对象存储或 etcd 变慢,导致段无法 sealed、growing 段堆积,查询被迫扫增量数据,延迟上升后引发重试,最后 QueryNode OOM、副本重分配,剩余节点继续过载。
处置要先分读写路径:查询慢先看 growing 段、索引构建和对象存储,入口限流与 release 冷 collection 往往比加节点快;写入积压则查 WAL 与 DataNode。重启会带来任务重放和段重载,通常不是第一步。Milvus 强在专用向量检索、规模与多租户,代价是组件多、依赖重、运维成本高;小规模且已有 PostgreSQL 或 ES 时,先用现成的往往更划算。