关于 victoriametrics-cardinality-analysis
该技能通过查询 VictoriaMetrics 的 TSDB 状态、指标使用统计和标签值模式,分析时间序列基数问题,适用于希望优化指标存储的数据运维人员。它会整理未使用指标、高基数标签、直方图开销和序列变化,并给出重标记或流聚合配置建议。
主要功能
- 01收集 TSDB 基数统计
- 02识别未使用和低频查询指标
- 03检查高基数标签及值模式
- 04分析直方图开销与序列变化
- 05生成重标记和流聚合建议
适用场景
- 01排查指标存储的序列数量增长
- 02筛选可删除的未使用指标
- 03发现包含请求 ID 或 SQL 的标签
- 04制定指标重标记与聚合方案
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ victoriametrics/ skills --skill victoriametrics-cardinality-analysis安装后在 Claude Code 中输入/victoriametrics-cardinality-analysis确认已加载。
详细介绍
VictoriaMetrics 基数分析
对 VictoriaMetrics 进行系统化的基数分析。收集 TSDB 状态、指标使用统计和标签值模式,然后生成结构化报告,其中包含用户可直接应用的具体重标记和流聚合配置。
目标是找出影响最大的优化机会——无人查询的指标、没有监控价值却导致基数激增的标签,以及表明数据卫生问题的模式(将错误消息、SQL 文本、UUID 作为标签)。
环境
使用与 victoriametrics-query 技能相同的环境变量:
# $VM_METRICS_URL - base URL
# cluster: export VM_METRICS_URL="https://vmselect.example.com/select/0/prometheus"
# single: export VM_METRICS_URL="http://localhost:8428"
# $VM_CURL_CONFIG - curl config file with auth header (unset if no auth is required)
# Remote: export VM_CURL_CONFIG="$HOME/.config/victoriametrics/curl.conf"
# Local: leave unset (defaults to /dev/null - no auth)
工作流
阶段 1:数据收集
在单次响应中启动 3 个子 Agent,并行收集数据。每个子 Agent 的提示词都必须包含上文中的 curl 身份验证模式和环境变量引用。
如果用户指定了范围(job、namespace、指标前缀),则将其作为 match[] 参数传递给 TSDB 状态查询,并作为序列选择器传递给标签查询。
子 Agent 1:TSDB 概览
Agent 名称:cardinality-tsdb | 描述:"收集 TSDB 基数统计"
查询 1 — 昨日的序列(捕获近期已流失的序列):
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/tsdb?topN=50&date=$(date -d 'yesterday' +%Y-%m-%d)" | jq '.data'
查询昨日的统计数据——范围比今天更广(包括可能已流失的序列),且无需扫描整个 TSDB。
查询 2 — 今日活跃序列:
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/tsdb?topN=50" | jq '.data'
查询 3 — 重点检查已知的高基数标签:
for label in pod instance container path url user_id request_id session_id trace_id le name; do
echo "=== focusLabel=$label ===" && \
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/tsdb?topN=20&focusLabel=$label" | \
jq --arg l "$label" '{label: $l, focus: .data.seriesCountByFocusLabelValue}'
done
返回内容:所有原始 JSON,保留结构。包含每个查询的 totalSeries、totalLabelValuePairs、
seriesCountByMetricName、seriesCountByLabelName、seriesCountByLabelValuePair。
子 Agent 2:指标使用统计
Agent 名称:cardinality-usage | 描述:"找出未使用和极少查询的指标"
查询 1 — 从未查询的指标:
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/metric_names_stats?le=0&limit=500" | jq '.'
查询 2 — 极少查询的指标(总查询次数 ≤5):
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/metric_names_stats?le=5&limit=500" | jq '.'
查询 3 — 统计概览(跟踪周期):
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/metric_names_stats?limit=1" | \
jq '{statsCollectedSince: .statsCollectedSince, statsCollectedRecordsTotal: .statsCollectedRecordsTotal}'
如果端点返回错误,可能是 vmstorage 上未启用 storage.trackMetricNamesStats。
在返回结果中注明这一点并继续——仅凭 TSDB 状态数据仍可进行分析。
查询 4 — 交叉检查:"未使用"的指标是否被告警规则引用?
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/rules" | jq '[.data.groups[].rules[].query]'
从规则查询中提取指标名称。任何出现在告警规则或记录规则中的"未使用"指标都不适合直接删除——它们会被间接查询。
返回内容:未使用指标及其与告警规则的交叉引用。将每项标记为:
- 可安全删除:从未查询,且未出现在任何规则中
- 仅被规则使用:仪表板从未查询,但被规则引用——请确认是否符合预期
- 极少使用:查询次数少,可能仅偶尔访问(例如,每月报告)
子 Agent 3:标签模式检查
Agent 名称:cardinality-labels | 描述:「检查标签值是否存在问题模式」
所有数据均来自 TSDB 状态端点——不要使用 /api/v1/labels 或 /api/v1/label/.../values。
查询 1 — 标签基数概览(唯一值数量 + 序列数量):
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/tsdb?topN=50" | \
jq '{labelValueCountByLabelName: .data.labelValueCountByLabelName, seriesCountByLabelName: .data.seriesCountByLabelName}'
labelValueCountByLabelName 会按唯一值数量对标签排序(取代逐个标签通过 /values 计数)。
seriesCountByLabelName 显示每个标签出现在哪些序列中,以及出现次数。
查询 2 — 通过 focusLabel 获取高基数标签的样本值: 对于查询 1 中唯一值超过 100 个的每个标签,获取样本值:
for label in <top labels from Query 1>; do
echo "=== focusLabel=$label ===" && \
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/tsdb?topN=20&focusLabel=$label" | \
jq --arg l "$label" '{label: $l, topValues: .data.seriesCountByFocusLabelValue}'
done
seriesCountByFocusLabelValue 会按序列数量对标签值排序——使用值名称检测问题模式。
查询 3 — 高基数标签值对:
curl -q --config "${VM_CURL_CONFIG:-/dev/null}" -s \
"$VM_METRICS_URL/api/v1/status/tsdb?topN=50" | \
jq '.data.seriesCountByLabelValuePair'
显示哪些特定的 label=value 对贡献了最多的序列。
模式检测 — 对 focusLabel 样本中的标签值进行分类:
| 模式 | 正则表达式提示 | 表示 |
|---|---|---|
| UUID | [0-9a-f]{8}-[0-9a-f]{4}- | 将请求/会话/跟踪 ID 用作标签 |
| IP 地址 | \d+\.\d+\.\d+\.\d+ | 按客户端或 Pod 跟踪 IP |
| 长字符串(>50 个字符) | 长度检查 | 错误消息、SQL、堆栈跟踪 |
| SQL 关键字 | SELECT|INSERT|UPDATE|DELETE|FROM|WHERE | 将查询文本存储为标签 |
| 含 ID 的 URL 路径 | /api/.*/[0-9a-f]+ | 未清理的 HTTP 路径 |
| 时间戳 | epoch 或 ISO8601 | 将时间值用作标签(无界) |
| 堆栈跟踪 | at .*\.(java|go|py): | 将错误详情用作标签 |
返回内容:按唯一值数量排序的标签表,包含检测到的模式、来自 focusLabel 的样本值以及序列影响。
第 2 阶段:分析
所有子 Agent 返回结果后,汇总并分类发现。这是分析的核心——要运用判断,而不是机械地筛选。
类别 1:未使用的指标(快速收益)
将指标使用统计与 TSDB 序列数量交叉核对:
- 可删除候选项:
queryRequestsCount=0,未出现在任何告警/录制规则中,且序列数 >100 - 待验证候选项:
queryRequestsCount=0,但被规则引用——检查这些规则是否仍有必要 - 低优先级:
queryRequestsCount≤5且序列较少——不值得引入配置变更
按序列数量降序排序——未使用指标占用越大,优化收益越大。
类别 2:高基数标签
唯一值过多、导致序列膨胀的标签:
| 标签模式 | 评估 | 常见解决方法 |
|---|---|---|
user_id、customer_id、account_id | 绝不应作为指标标签——应放在日志/跟踪中 | 删除标签 |
request_id、session_id、trace_id、span_id | 关联 ID——绝不应作为指标标签 | 删除标签 |
error、error_message、reason、status_message | 无界字符串 | 删除标签或替换为错误代码 |
sql、query、command、statement | 标签中包含查询文本——无界 | 删除标签 |
path、url、uri、endpoint | 未清理时无界 | 通过重新标记进行规范化,或不按其流式聚合 |
pod、container | 对 k8s 来说很常见,但变动频繁 | 如果不需要按 Pod 统计,则不按其流式聚合 |
instance | 对节点指标来说很常见,对应用指标来说则浪费资源 | 对应用级指标不按其流式聚合 |
le(直方图桶) | 细粒度的桶会使每种标签组合对应的序列数倍增 | 减少桶数量 |
对于每项发现,估算影响:(包含此标签的序列数)-(不包含此标签的序列数)≈ 节省的序列数。
类别 3:直方图膨胀
检查以 _bucket 结尾的指标:
- 有多少个唯一的
le值? - 每增加一个桶,序列数就会乘以(标签组合数量)
- 查找大多数桶都为空或重复的直方图
类别 4:序列 churn
比较昨天和今天的统计数据:
- 比例 >3:1 表明 pod 重启、部署、短时任务导致了明显的 churn
- 无法直接通过重新标记修复,但表明可以调整
dedup_interval或-search.maxStalenessInterval
阶段 3:报告
整理成结构化报告。每项发现都必须包含影响估算和具体的修复配置。
使用此模板:
## VictoriaMetrics Cardinality Report — <date>
### Overview
| Metric | Value |
|--------|-------|
| Total active series (today) | X |
| Total series (yesterday) | X |
| Churn ratio (yesterday / today) | X:1 |
| Unique metric names | X |
| Stats tracking since | <date> |
### 1. Unused Metrics
**Potential savings: ~X series (Y% of total)**
| Metric | Series | Last Queried | In Alert Rules | Action |
|--------|--------|-------------|----------------|--------|
| ... | ... | never | no | Drop |
| ... | ... | never | yes — verify | Check rule |
<details>
<summary>Relabeling config to drop unused metrics</summary>
```yaml
# Add to vmagent metric_relabel_configs (VMServiceScrape or global)
metric_relabel_configs:
- source_labels: [__name__]
regex: "metric1|metric2|metric3"
action: drop
```
</details>
### 2. High-Cardinality Labels
**Potential savings: ~X series (Y%)**
| Label | Unique Values | Top Affected Metrics | Pattern | Action |
|-------|--------------|---------------------|---------|--------|
| user_id | 50,000 | http_requests_total | UUID | Drop |
| path | 10,000 | http_request_duration | URL paths | Aggregate |
| error_message | 5,000 | app_errors_total | Long strings | Drop |
<details>
<summary>Drop labels that should never be in metrics</summary>
```yaml
metric_relabel_configs:
- regex: "user_id|request_id|session_id|trace_id|error_message|sql_query"
action: labeldrop
```
</details>
<details>
<summary>Stream aggregation for high-cardinality HTTP labels</summary>
```yaml
# vmagent stream aggregation config
- match: '{__name__=~"http_request.*"}'
interval: 1m
without: [path, instance, pod]
outputs: [total]
# drop_input: true # enable after verifying aggregated output
- match: '{__name__=~"http_request_duration.*_bucket"}'
interval: 1m
without: [pod, instance]
outputs: [total]
keep_metric_names: true
```
</details>
<details>
<summary>Normalize URL paths via relabeling</summary>
```yaml
metric_relabel_configs:
- source_labels: [path]
regex: "/api/v1/users/[^/]+"
target_label: path
replacement: "/api/v1/users/:id"
- source_labels: [path]
regex: "/api/v1/orders/[^/]+"
target_label: path
replacement: "/api/v1/orders/:id"
```
</details>
### 3. Histogram Optimization
**Potential savings: ~X series (Y%)**
| Metric | Bucket Count | Recommendation |
|--------|-------------|----------------|
| ... | 30 | Reduce to standard 11 buckets |
### 4. Series Churn
| Observation | Value |
|------------|-------|
| Yesterday / today ratio | X:1 |
| Primary driver | Pod restarts / short-lived jobs |
### Summary
| Category | Est. Series Saved | % of Total | Effort |
|----------|------------------|-----------|--------|
| Drop unused metrics | X | Y% | Low — relabeling only |
| Drop bad labels | X | Y% | Low — labeldrop |
| Stream aggregation | X | Y% | Medium — new config |
| Histogram reduction | X | Y% | Low — bucket filtering |
| **Total** | **X** | **Y%** | |
### Implementation Priority
1. **[Low effort]** Drop unused metrics — pure relabeling, no data loss risk
2. **[Low effort]** Drop labels that should never be in metrics (IDs, messages, SQL)
3. **[Medium effort]** Stream aggregation for high-cardinality HTTP/app metrics
4. **[Medium effort]** Histogram bucket reduction
根据实际发现调整模板——省略没有发现的部分,并扩展有重要发现的部分。
修复参考
重新标记(metric_relabel_configs)
在抓取时或远程写入时应用。更改会立即影响新数据。
丢弃整个指标:
metric_relabel_configs:
- source_labels: [__name__]
regex: "metric_to_drop|another_metric"
action: drop
丢弃标签:
metric_relabel_configs:
- regex: "label_to_drop|another_label"
action: labeldrop
规范化标签值(减少唯一值数量):
metric_relabel_configs:
- source_labels: [path]
regex: "/api/v1/users/[^/]+"
target_label: path
replacement: "/api/v1/users/:id"
流聚合
在 vmagent 层应用。在写入存储之前聚合正在传输的数据。 文档:https://docs.victoriametrics.com/victoriametrics/stream-aggregation/
移除标签,同时保留指标语义:
- match: '{__name__=~"http_.*"}'
interval: 1m
without: [instance, pod]
outputs: [total]
聚合计数器(丢弃高基数维度):
- match: 'http_requests_total'
interval: 30s
without: [path, user_id]
outputs: [total]
聚合直方图:
- match: '{__name__=~".*_bucket"}'
interval: 1m
without: [pod, instance]
outputs: [quantiles(0.5, 0.9, 0.99)]
keep_metric_names: true
常用输出函数:
| 函数 | 用途 | 示例 |
|---|---|---|
total | 计数器(运行总和) | 请求数 |
sum_samples | 仪表盘求和 | pod 间的内存使用量 |
count_samples | 样本计数 | 上报数据的实例数量 |
last | 最新的仪表盘值 | 当前温度 |
min、max | 极值 | 峰值延迟 |
avg | 平均值 | 平均 CPU 使用率 |
quantiles(0.5, 0.9, 0.99) | 分布估算 | 延迟百分位数 |
histogram_bucket | 重新划分直方图桶 | 降低桶粒度 |
重要:计数器使用 total,仪表盘使用 last/avg/sum_samples。对仪表盘使用 total 会产生毫无意义的运行总和。
在 Kubernetes 中的应用位置
| 方法 | CRD / 配置 | 范围 |
|---|---|---|
metric_relabel_configs | VMServiceScrape / VMPodScrape .spec.metricRelabelConfigs | 每个抓取目标 |
| 全局重新标记 | VMAgent -remoteWrite.relabelConfig | 所有指标 |
| 流聚合 | VMAgent -remoteWrite.streamAggr.config | 所有远程写入的指标 |
| 按远程写入配置的 SA | VMAgent .spec.remoteWrite[].streamAggrConfig | 每个目标 |
常见错误
| 错误 | 修复方法 |
|---|---|
| 丢弃告警使用的指标 | 丢弃前务必检查 /api/v1/rules |
未经测试就设置 drop_input: true | 先确认聚合输出符合预期 |
使用 total 聚合仪表盘 | 仪表盘使用 last、avg 或 sum_samples |
忘记设置 keep_metric_names: true | 不设置时,输出会得到很长的自动生成后缀 |
从直方图中完全丢弃 le 标签 | 只能丢弃特定的 le 值,绝不能丢弃该标签本身 |
| 未考虑记录规则依赖关系 | 检查告警规则和记录规则 |
| 未经测试就应用重新标记 | 使用 -dryRun 标志,或先在单个抓取目标上测试 |