关于 vm-trace-analyzer
vm-trace-analyzer 是一项从 VictoriaMetrics 查询跟踪 JSON 生成性能报告的技能。它通过脚本汇总跟踪数据并检查较新版本的变更日志,再定位主要耗时阶段并给出基于证据的建议。
主要功能
- 01解析跟踪数据并生成摘要
- 02检查跟踪树和匹配节点
- 03按查询阶段拆分耗时
- 04报告各 vmstorage 节点详情
- 05检查较新版本的 VictoriaMetrics 变更日志
- 06依据瓶颈选择建议
适用场景
- 01诊断缓慢的 PromQL 查询
- 02比较各 vmstorage 节点的查询阶段
- 03查看较新版本中的性能修复
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ victoriametrics/ skills --skill vm-trace-analyzer安装后在 Claude Code 中输入/vm-trace-analyzer确认已加载。
附带文件scripts/check_changelog.pyscripts/parse_trace.py
详细介绍
VictoriaMetrics 查询跟踪分析器
你正在分析 VictoriaMetrics 查询跟踪——一种记录 PromQL 查询执行每个步骤的 JSON 树。你的目标是读取这棵树,了解执行过程,并生成清晰的性能报告及可行的改进建议。
背景
在 Cluster 模式下,查询处理涉及两个组件:
- vmselect — 查询前端,负责接收 PromQL 或 MetricsQL 查询、从 vmstorage 节点获取数据并执行计算
- vmstorage — 存储时间序列数据,并通过 RPC 将数据提供给 vmselect
Single-node 模式在一个进程中运行所有功能。跟踪结构类似,但不包含 RPC 封装。
你可以根据跟踪中的根消息判断所处的模式:
- Cluster 跟踪包含
vmselect-<version>: /select/..., - Single-node 跟踪包含
/victoria-metrics-<version>: /api/v1/...。
什么是查询跟踪?
在 VictoriaMetrics HTTP 查询中添加 trace=1 后,它会返回一棵描述每项内部操作的 JSON 树。
每个节点如下所示:
{
"duration_msec": 123.456,
"message": "description of what happened",
"children": [ ... ]
}
这棵树以 vmselect 为根节点,记录了完整的查询执行流程:解析、序列搜索、从存储中获取数据、rollup 计算、聚合和响应生成。
如何分析跟踪
步骤 0:运行解析脚本
手动阅读跟踪文件之前,先运行分析脚本提取结构化数据:
python3 <skill_base_dir>/scripts/parse_trace.py <trace_file>
该脚本会输出:根节点信息、跟踪树(深度为 3)、包含耗时的关键节点、各 vmstorage 的 RPC 明细,以及计算得出的总量(字节数、样本数、序列数)。将此输出作为报告的主要数据来源。
用于深入调查的其他子命令:
python3 <script> <trace> tree --depth N— 打印深度为 N 的跟踪树python3 <script> <trace> nodes --pattern "fetch unique"— 查找所有包含指定子字符串的节点
只有在摘要输出存在歧义或信息缺失时,才进一步深入调查。
运行摘要后,还要检查较新 VictoriaMetrics 版本中的相关性能改进:
python3 <skill_base_dir>/scripts/check_changelog.py <version> <mode>
其中,<version> 是解析脚本输出的语义化版本号(例如 v1.130.0),<mode> 为 cluster 或 single-node。此命令会从 GitHub 获取变更日志,并显示跟踪采集版本之后版本中与性能相关的修复和功能。如果获取失败,请跳过此部分。
步骤 1:从根节点开始
读取用户提供的跟踪 JSON 文件(或使用步骤 0 的脚本输出)。 根节点展示了整体情况。提取以下信息:
- Endpoint:
/api/v1/query(即时查询)或/api/v1/query_range(范围查询) - Query:
query=后面的 PromQL 表达式 - 时间参数:范围查询的
start=、end=、step= - 结果数量:末尾的
series= - 总耗时:根节点的
duration_msec - 版本:跟踪开头显示的版本。
步骤 2:识别各阶段
遍历顶层子节点,并将每个子节点归类到以下某个阶段。 并非每条跟踪记录都包含所有阶段,只需报告实际出现的阶段。
对于较大的跟踪记录,先关注顶层子节点。 仅当子树与瓶颈相关或耗时异常时,才深入查看。
查询跟踪记录通常包含以下阶段,顺序大致如下。 并非每条跟踪记录都会出现所有阶段。请根据此处描述的消息模式识别各阶段。
表达式求值 — 匹配以下内容的节点:eval: query=..., timeRange=..., step=..., mayCache=...: series=N, points=N, pointsPerSeries=N
这些节点记录 PromQL/MetricsQL 表达式树的递归求值。
这些节点记录 PromQL/MetricsQL 表达式树的递归求值。
每个 eval 节点都可能包含子表达式对应的子节点。关键数字:
- series — 此子表达式生成的时间序列数
- points — 所有时间序列的数据点总数
- pointsPerSeries — 每条时间序列的数据点数
函数与聚合 — 匹配以下内容的节点:
transform <func>(): series=N— PromQL 函数(histogram_quantile、clamp 等)aggregate <func>(): series=N— 聚合运算符(sum、avg、max 等)binary op "<op>": series=N— 二元运算
序列搜索(索引查找) — 根据标签匹配器查找对应的内部序列 ID:
- 在 Cluster 跟踪记录中,包含在
rpc at vmstorage <addr>→rpc call search_v7()之下;在 Single-node 中,会直接出现,不带 RPC 封装 - 关键消息:
init series search,search TSIDs,search N indexDBs in parallel— 并行搜索索引数据库,search indexDB— 单个索引分区,found N metric ids for filter=...— metric ID,即 vmstorage 内唯一的时间序列标识符,found N TSIDs for N metricIDs— 与 metric ID 相同,sort N TSIDs
- 此阶段与缓存相关的消息:
search for metricIDs in tag filters cache后跟cache miss或缓存命中(没有cache miss子节点)put N metricIDs in cache/stored N metricIDs into cache
数据获取 — 获取原始数据:
- Cluster:
fetch matching series: ...包含对各 vmstorage 节点的 RPC 调用:rpc at vmstorage <addr>— 每个节点的 RPC,sent N blocks to vmselect— 传回的数据块数量,fetch unique series=N, blocks=N, samples=N, bytes=N— 所有 vmstorage 节点的汇总数据,
- Single-node:
search for parts with data for N series后跟数据扫描消息。fetch unique series中的 bytes 值表示传输的数据总量,是 I/O 负载的良好指标。
汇总计算 — 计算 rate()、increase()、avg_over_time() 等:
rollup <func>(): timeRange=..., step=N, window=Nrollup <func>() with incremental aggregation <agg>() over N series— 这是一种优化the rollup evaluation needs an estimated N bytes of RAM for N series and N points per series— 内存估算parallel process of fetched data: series=N, samples=N— 对原始样本执行的实际计算series after aggregation with <func>(): N; samplesScanned=N— 聚合后的结果 对于扫描大量原始数据的查询,此阶段通常占据大部分执行时间。
响应生成 — 通常非常简单:
sort series by metric name and labelsgenerate /api/v1/query_range response for series=N, points=N通常速度非常快。如果响应很大(数百条序列,每条序列有数千个数据点),且客户端读取响应的速度较慢,则此阶段可能成为瓶颈。
步骤 3:细分耗时
记录每个阶段的 duration_msec。
在 Cluster 跟踪记录中,相同阶段会在每个 vmstorage 节点上重复出现 — 汇总时应计算总量,同时也要记录各节点的数据,以发现不均衡情况。
步骤 4:找出瓶颈
找出耗时最长的阶段,并具体说明原因。 例如,“rollup 扫描了 2.12 亿条原始样本”是有用的信息;“查询很慢”则不是。
步骤 5:编写建议
建议只能基于追踪实际显示的信息。 如果查询快速且运行良好,就如实说明——不要凭空捏造问题。
按照以下算法选择建议:
- 步骤 5a: 根据耗时明细,找出占总延迟比重最大的单个阶段(>60%)。将其对应到下方“建议模式”部分中的匹配模式。
- 步骤 5b: 仅使用该模式中的建议,并按照列出的优先级排序。不要从其他模式中提取建议。
- 步骤 5c: 如果没有任何单个阶段超过 60%,选择占比最高的模式,并指出次要因素,但仍不要混用不同模式中的建议。
报告格式
## Query Overview
- **Query:** `<the PromQL/MetricsQL expression>`
- **Type:** instant / range query
- **Time range:** <start> to <end> (<duration>)
- **Step:** <step>
- **Result:** <N> series, <N> points
- **Version:** vmselect or VM single-node version
## Performance Summary
- **Total duration:** <N>ms
- **Duration score:** <Fast / Acceptable / Slow / Very Slow>
- **Matched series:** <N> (across all storage nodes)
- **Raw samples scanned:** <N>
- **Bytes transferred:** <N>
"Duration score" thresholds:
- Fast: < 500ms
- Acceptable: 500ms–5s
- Slow: 5s–10s
- Very Slow: > 10s
## Execution Time Breakdown
| Phase | Duration | % of Total | Notes |
|-------|----------|------------|-------|
| Series search (index) | Xms | X% | |
| Data fetch | Xms | X% | |
| Rollup computation | Xms | X% | |
| Aggregation / functions | Xms | X% | |
| Response generation | Xms | X% | |
(Adapt the phases to what actually appears in the trace.
For cluster traces, break down data fetch per storage node.)
## Storage Node Breakdown (cluster only)
| Node | Series | Bytes sent | Duration |
|------|--------|-------------|----------|
| vmstorage-1 | N | N | Xms |
| vmstorage-2 | N | N | Xms |
## Bottleneck Analysis
Name the single biggest contributor to total query time. Explain why it's slow with specific numbers from the trace.
## Recommendations
Provide actionable suggestions to reduce query latency (see guidance below).
## Upgrade Recommendations (if applicable)
If the changelog check found performance-relevant improvements in newer versions,
list them here with version, release date, and description.
Only include this section if there are concrete relevant entries. Omit entirely otherwise.
报告生成规则
- 不要推测追踪中没有证据支持的问题。如果追踪显示运行良好且查询快速,就如实说明。
- 报告中不要展示块的信息——这对用户没用,还可能造成困惑。重点关注字节数。
- 除非 vmstorage 节点之间的 Duration 相差超过 1-2s,否则不要提及这种差异。
- 耗时使用“Xm”/“Xs”/“Xms”格式(例如“123ms”);仅当耗时超过 60s 时使用分钟,超过 1000ms 时使用秒,更短的耗时使用毫秒。
- 数据量使用易读格式(例如,用“268 MB”代替“268000000 bytes”)。根据大小选择合适的单位(KB、MB、GB)。
- 直接使用追踪中的
duration_msec值——不要通过相减估算耗时。 - 在集群追踪中,相同的阶段(索引搜索、数据扫描)会出现在每个 vmstorage 节点中。汇总时合并这些阶段,同时也展示各节点的明细,以便用户发现负载不均。
- 如果追踪太大,无法一次读完,请先阅读顶层结构,根据
duration_msec找出最慢的分支,再深入查看这些分支。 - 某些追踪可能包含
subquery或@ modifier求值——应将其视为嵌套的 eval 阶段。 - eval 消息中的
mayCache=false仅供参考,并不代表存在问题
建议模式
重要:模式选择规则
- 首先,根据耗时明细确定以下哪一种模式与瓶颈相符。
- 仅使用该模式中的建议,并按列出的优先级排序。
- 不要混用不同模式的建议。如果主要阶段占总延迟的 60% 以上,则所有建议都必须仅来自该模式。
- 如果某项建议出现在多个模式中,并不意味着它与模式无关——仅当它列在你所选的模式中时才使用。
根据追踪实际显示的情况,按优先级提出建议。 以下是常见模式及相应建议:
序列基数高(匹配的序列很多)
- 建议添加更具体的标签匹配器,以减少序列数量
- 如果这是仪表板查询,建议使用记录规则进行预聚合
- 如果可行,建议使用流式聚合,在存储前对序列进行预聚合
- 如果匹配的指标具有较高的变更率,建议缩短时间范围
原始样本扫描量大(samplesScanned 高)
- 如果 samplesScanned 的数量远超获取的样本数量,则说明查询使用的
[window]过短或子查询过于激进。 - 对于
rate()/irate()/increase(),如果语义上可接受,建议使用较短的[window] - 建议增大查询步长,以减少每条序列的数据点数量
- 建议缩短时间范围
索引查找缓慢(序列搜索占主导)
- 标签筛选器缓存未命中在首次查询时很正常;请说明重复查询应该会更快
- 每日分区中的指标 ID 数量很多,说明可能存在高变更率或高基数问题
- 如果可行,建议使用选择性更高的筛选器
- 如果可行,建议增加 vmstorage 节点的内存
数据获取缓慢/传输字节数高(集群)
- 发送字节数较大,说明 vmstorage 正在进行大量 I/O
- 如果资源达到饱和,建议在 VictoriaMetrics 官方 Grafana 仪表板中检查资源使用情况。
- 建议检查 vmstorage 的磁盘 I/O 和网络带宽
- 多个 vmstorage 节点的耗时差异很大(超过 1~2 秒),可能表明资源饱和或存在硬件问题。
- 如果可行,建议增加更多 vmstorage 节点,以横向扩展 I/O 容量
数据获取缓慢/传输字节数高(单节点)
- 发送字节数较大,说明 VM 单节点正在进行大量 I/O
- 如果资源达到饱和,建议在 VictoriaMetrics 官方 Grafana 仪表板中检查资源使用情况。
- 建议检查 VM 单节点的磁盘 I/O 和 CPU
- 建议提高磁盘 I/O 限制
- 如果无法优化查询或查询的数据,建议切换到集群拓扑,以横向扩展 I/O 容量
汇总计算占主导(通常由扫描数百万个原始样本导致)
- 建议提高 vmselect 的 CPU 限制,以加快计算速度。
- 建议添加更具体的标签匹配器,以减少待处理的序列数量。
- 建议缩短时间范围,以减少原始样本量。
- 建议增大查询
step,以减少每条序列的数据点数量。 - 对于重复执行的仪表板查询,建议使用记录规则预先计算结果。
版本升级机会
如果 check_changelog.py 脚本发现了较新版本中的相关性能改进,请在“升级建议”报告部分中将升级列为附加建议。这是“仅使用单一模式”规则的唯一例外——无论选择了哪种瓶颈模式,都可以补充升级建议。仅纳入与观察到的瓶颈或追踪中涉及的组件直接相关的条目。