指标#
指标通过 OpenTelemetry 计数器收集,并通过进程内的 Prometheus /metrics HTTP 端点导出(默认端口 9090)。当设置 --otlp-endpoint 时,指标也会推送到 OTel 收集器。
所有指标使用 lmcache_mp. 前缀(多进程)。在 Prometheus 中,点被转换为下划线,计数器会添加 _total 后缀(例如 lmcache_mp_l1_read_chunks_total)。
全局资源属性#
每个由 MP 服务器导出的指标和跨度都携带在启动时构建的资源级属性。这些属性标识生成遥测的进程,并与每个指标的属性(如 cache_salt)正交。
属性 |
命令行标志 / 配置 |
未设置时的默认值 |
|---|---|---|
|
|
在启动时生成的随机 UUID v4。 |
资源属性附加到 MeterProvider / TracerProvider 并通过 OTLP 传播到每个导出的数据点。在 Prometheus 中,SDK 资源属性出现在 target_info 系列上,而不是每个时间序列上——这是标准的 OTel 行为。
L1 指标#
指标 |
类型 |
描述 |
|---|---|---|
|
计数器(属性: |
按租户分组的从 L1 读取的块数。 |
|
计数器(属性: |
按租户分组写入 L1 的块数。 |
|
计数器(属性: |
按租户分组的被 EvictionController 逐出的块数。 |
|
计数器 |
L1 逐出循环迭代(每个周期,无论水位线是否被跨越)。由 |
|
计数器 |
L1 逐出循环迭代,其中 |
L1 块生命周期直方图#
通过 L1LifecycleSubscriber 进行采样的(默认 1%)块级生命周期跟踪。只有采样的块会对直方图产生贡献;上述计数器始终计算所有事件。采样是确定性的(基于哈希),因此相同的键总是会得到相同的决策,且没有内存开销。
指标 |
类型 |
描述 |
|---|---|---|
|
直方图 |
每个采样块从分配到逐出的时间。 |
|
直方图 |
每个采样块从最后访问到逐出的时间。 |
|
直方图 |
同一块的连续访问(读取或写入)之间的时间间隔。 |
|
直方图 |
逐出到下次重用的时间(上限为 300 秒)。 |
存储管理器真实重用指标#
由 SMLifecycleSubscriber 发出的工作负载级重用直方图,由面向调用者的 StorageManager 事件(SM_READ_PREFETCHED_FINISHED,SM_WRITE_FINISHED)驱动。存储/预取控制器的内部读取锁释放被排除在外,因此信号仅反映用户驱动的访问。
这两个直方图都带有 cache_salt 标签,以实现租户隔离。每个块的每次读取和写入都会使对应 cache_salt 的访问计数器递增(无论是否采样),因此块间隔反映了真实的存储量;直方图本身仅记录通过(确定性、基于哈希的)采样门控的块的间隔。
指标 |
类型 |
描述 |
|---|---|---|
|
直方图(标签: |
块的最后一次访问(读取或写入)与下一次读取之间的时间间隔。捕获存储成本——一个存储块在访问之间静止了多久。仅在读取事件中发出。 |
|
直方图(标签: |
同一块两次读取之间每 |
L2 指标#
指标 |
类型 |
描述 |
|---|---|---|
|
计数器 |
提交的 L2 存储请求数量。 |
|
计数器(属性: |
按租户分组的提交到 L2 存储的块数量。 |
|
计数器(属性: |
按适配器类型标记的完成的 L2 存储请求数量。 |
|
计数器(属性: |
按租户分组成功存储到 L2 的块数。 |
|
计数器 |
L2 预取查找请求的数量。 |
|
计数器(属性: |
按租户分组的提交用于 L2 预取查找的块数量。 |
|
计数器 |
在 L2 查找中找到的前缀块数量。 |
|
计数器 |
提交的 L2 预取加载请求数量。 |
|
计数器(属性: |
按租户分组的提交用于 L2 加载的块数量。 |
|
计数器(属性: |
按租户分组成功从 L2 加载的块数。 |
|
计数器(属性: |
按适配器类型标记的每个适配器完成的 L2 加载请求数量。 |
|
计数器(属性: |
按租户分组的从 L2 逐出的块数量。 |
l2_name 标签的计数器 (l2_store_completed 和 l2_load_completed) 存在的目的是为了让仪表板能够通过 rate(lmcache_mp_l2_store_completed_requests_total{l2_name=\"...\"}[1m]) 按需计算每个后端的 IOPS(加载的等效项也是如此)。没有单独导出 *_iops 指标;保留原始计数器让仪表板用户可以选择自己的时间窗口。
失败与健康计数器#
在专用的 lmcache_mp.health OTel 计量器上发出的健康监测计数器。由 L1FailureMetricsSubscriber 和 L2FailureMetricsSubscriber 驱动,这两个订阅者在启用指标时会自动注册。所有三个计数器都携带 model_name(从每个 ObjectKey 中提取),以便操作员可以在 Prometheus /metrics 端点上按模型进行切片。
指标 |
类型 |
描述 |
|---|---|---|
|
计数器 |
在 |
|
计数器 |
L1 |
|
计数器 |
L2 报告在查找时存在但未能进入 L1 的块。标记为 |
一旦 L2 适配器区分反序列化错误和缺失对象,将会将 reason=serde_failure 值作为附加的、非破坏性的扩展添加到 l2_prefetch_failure 中——当这项功能上线时,无需进行仪表板迁移。
有关完整的设计原理(包括哪些事件类型驱动每个计数器以及为何推迟 lmcache_instance_id),请参阅源树中的 docs/design/v1/mp_observability/METRICS.md。
查找命中率指标#
令牌级计数器,其比值表示通过查找请求的令牌中有多少比例是从 L1 或 L2 提供的。L0(GPU 前缀缓存)被有意排除在外——它由 vLLM 管理,无法从 LMCache 中观察到。
指标 |
类型 |
描述 |
|---|---|---|
|
计数器(属性: |
提交查找的总令牌数(L1+L2 令牌级命中率的分母)。仅计算与块对齐的令牌。 |
|
计数器(属性: |
在查找过程中在 L1 或 L2 中找到的总令牌数(L1+L2 令牌级命中率的分子)。仅计算连续前缀命中。 |
这两个计数器由同一事件(MP_LOOKUP_PREFETCH_END)驱动,因此它们在每次完成查找时总是一起增加。提前退出的查找对两者都贡献 0,而放弃的查找则不对两者贡献。
model_name 和 cache_salt 属性在查找时从 IPCCacheServerKey 捕获,以便仪表板计算每个模型或每个租户的命中率。cache_salt 可能具有高基数(每个租户或隔离域一个条目);若存储成本敏感,请在抓取时通过 metric_relabel_configs 丢弃它。
PromQL 查询命中率:
# Aggregate (all models, all salts):
rate(lmcache_mp_lookup_hit_tokens_total[5m])
/ rate(lmcache_mp_lookup_requested_tokens_total[5m])
# Per-model:
sum(rate(lmcache_mp_lookup_hit_tokens_total[5m])) by (model_name)
/ sum(rate(lmcache_mp_lookup_requested_tokens_total[5m])) by (model_name)
L0 (GPU) 块生命周期直方图#
通过 L0LifecycleSubscriber 采样(默认 1%)GPU KV Cache 块生命周期跟踪。在重新分配时检测逐出(当块被分配不同的令牌时)。采样使用带有 _skipped 集的随机选择(受限于有限数量的物理 GPU 块)。
所有 L0 直方图都带有 instance_id 和 model_name OTel 属性,从而在 Prometheus 中实现按实例和按模型的指标切片(例如 lmcache_mp_l0_block_lifetime_seconds{instance_id=\"12345\",model_name=\"llama-7b\"})。
指标 |
类型 |
描述 |
|---|---|---|
|
直方图 |
每个采样的 GPU 块从分配到逐出的时间。 |
|
直方图 |
每个采样的 GPU 块从最后访问到逐出的时间。 |
|
直方图 |
同一 GPU 块连续访问之间的时间间隔。 |
L0 ↔ L1 吞吐量直方图#
每个请求的 GPU↔CPU 复制吞吐量通过 L0L1ThroughputSubscriber 进行统计。每个存储/检索请求为相应的直方图贡献一个样本:total_bytes / (end_ts - start_ts) 以 GB/s 为单位。时间戳来自在 GPU cupy 流上发布的 MP_{STORE,RETRIEVE}_{START,END} 事件,因此它们反映了真实的 GPU 流复制时间——而不是 Python/锁的开销。
所有吞吐量直方图都带有 engine_id(vLLM 工作实例 ID)、device(例如 "cuda:3")和 model_name OTel 属性,从而支持在 Prometheus 中按工作者、设备和模型进行切片(例如 lmcache_mp_l0_l1_store_throughput_GB_per_second{engine_id="0",device="cuda:3",model_name="meta-llama/Llama-3.1-8B"})。
指标 |
类型 |
描述 |
|---|---|---|
|
直方图 |
每个请求的 GPU→CPU (L0→L1) 存储吞吐量(单位:GB/s)。 |
|
直方图 |
每个请求的 CPU→GPU (L1→L0) 加载吞吐量(GB/s)。 |
L1 ↔ L2 吞吐量直方图#
每个请求的 L1↔L2 传输吞吐量通过 L2ThroughputSubscriber 进行统计。存储路径通过 (adapter_index, task_id) 关联 L2_STORE_SUBMITTED → L2_STORE_COMPLETED。加载路径通过 (request_id, adapter_index) 关联每个适配器的 L2_LOAD_TASK_SUBMITTED → L2_LOAD_TASK_COMPLETED 事件;请求级别的 L2_PREFETCH_LOAD_* 事件用于块计数器在适配器之间进行聚合,无法归因于特定的 l2_name。
时间戳跨度为 提交 → 完成,因此持续时间包括适配器队列、网络和磁盘 I/O — 该值为 字节 / 端到端延迟,而非原始传输速率。使用这些直方图比较适配器类型并捕捉回归;当您需要纯粹的复制时间吞吐量时,请使用 L0↔L1 直方图。
所有 L1↔L2 吞吐量直方图都携带一个 l2_name OTel 属性——注册的适配器类型(例如 \"fs\", \"nixl_store\", \"mooncake_store\")——使得在 Prometheus 中能够按后端进行切片(例如 lmcache_mp_l2_store_throughput_GB_per_second{l2_name=\"nixl_store\"})。
指标 |
类型 |
描述 |
|---|---|---|
|
直方图 |
每个请求的 L1→L2 存储吞吐量(GB/s)。 |
|
直方图 |
每对(请求,适配器)的 L2→L1 加载吞吐量(单位:GB/s)。 |
引擎计数器#
与 MP 服务器通过 retrieve() 返回给每个 vLLM 工作线程相关的工作线程范围计数器。以 worker_id(vLLM 工作线程实例 ID)标记——与其他指标中可能出现的任何调度器范围 ID 不同。
指标 |
类型 |
描述 |
|---|---|---|
|
计数器(属性: |
加载到引擎的 LMCache 块总数,汇总自所有 |
可观察仪表#
通过 register_gauge 注册的即时状态快照(基于拉取的 OTel 可观察仪表)。
这三个正在进行的指标携带两个属性,即使在与相同后端类型注册多个适配器时也能区分它们——与 lmcache_mp.l2_store_completed 具有相同形状:
l2_name— 注册的适配器类型(例如"fs","nixl_store","mooncake_store")。adapter_index— 控制器适配器列表中的位置。
没有正在进行的工作的适配器不会为该抓取发出数据点。
指标 |
类型 |
描述 |
|---|---|---|
|
可观察仪表 |
当前正在进行的预取作业数量。持续的高值可能表示 L2 后端缓慢或轮询延迟。 |
|
可观察仪表 |
当前在 L1 中占用的字节数。持续上升而没有平台期通常表示存在泄漏;在配置的 |
|
可观察仪表 |
L1 使用/总比率 ( |
|
可观察仪表 (属性: |
每个 L2 适配器当前持有的字节数,在抓取时从 |
|
ObservableGauge (attrs: |
每个适配器当前正在执行的 L2 存储任务。 持续的非零值表明适配器无法跟上 L1 → L2 写入速率。 |
|
ObservableGauge (attrs: |
每个适配器当前正在执行的 L2 → L1 预取加载任务。与 |
|
ObservableGauge (attrs: |
每个适配器由正在进行的 L2 → L1 预取加载所预留的 L1 字节数。若正在进行的字节数与 |
事件总线自我监控#
EventBus 本身的健康指标,由 EventBusSelfMetricsSubscriber 在 lmcache.event_bus OTel 计量器上注册。这些指标通过 EventBus 访问器直接观察总线状态,并在每次 OTel 抓取时报告——它们不是由事件驱动的,因此丢弃或失败的订阅者无法使其静默。
使用它们来回答:EventBus 是否能跟上发布者的速度,是否有事件被丢弃,是否有订阅者回调抛出异常?非零的 dropped_events_total 或持续非零的 drain_lag_seconds 表明总线队列已达 --event-bus-queue-size 上限并正在进行尾部丢弃;请调大该参数值或排查慢速订阅者。
指标 |
类型 |
描述 |
|---|---|---|
|
可观察仪表 |
事件总线中当前排队的事件(在抓取时的 |
|
可观察仪表 |
自最旧的排队事件发布以来的秒数;队列为空时为 |
|
可观察计数器 |
因 EventBus 队列达到 |
|
可观察计数器 (属性: |
由事件总线分发期间由订阅者回调引发的累计异常,按 |
有关完整的设计原理以及支持每个指标的进程内访问器,请参阅源代码树中的 docs/design/v1/mp_observability/METRICS.md 和 docs/design/v1/mp_observability/event-bus.md。
Prometheus 抓取配置#
将 LMCache 服务器添加为 Prometheus 抓取目标:
scrape_configs:
- job_name: "lmcache-mp"
static_configs:
- targets: ["<lmcache-host>:9090"]