メトリクスの基数爆発とその抑制
監視基盤が突然重くなる原因を断つ。ラベルの直積で時系列が指数的に膨らむ基数爆発の機構と、集約・サンプリング・exemplarによる抑制原理をTSDBの内部構造から理解できます。
- 1.時系列数はメトリクス名とラベル値の直積で決まり、ラベルを掛け合わせるほど指数的に増える。これが基数爆発。
- 2.PrometheusのTSDBは時系列ごとにメモリ上のheadチャンクと索引を持つため、基数の増大はメモリ・索引・クエリ全体を直撃する。
- 3.抑制は3層。高基数ラベルを集約で落とす/recording ruleで事前集約/個別の事例はexemplarでトレースへ逃がす。
基数(cardinality)とは何か
メトリクスの世界で言う 基数(cardinality) とは、区別される時系列(time series)の総数 を指します。Prometheus において1本の時系列は「メトリクス名」と「ラベルの集合」の組で一意に決まります。たとえば次は別々の時系列です。
http_requests_total{method="GET", status="200", path="/login"}
http_requests_total{method="POST", status="500", path="/cart"}
同じ http_requests_total という名前でも、ラベルの値が1つでも違えば 完全に独立した時系列 として扱われます。ここが基数を理解する出発点です。集計済みで軽いはずのメトリクスが基盤を圧迫する原因は、ほぼすべてこの「時系列の本数」に帰着します。
基数爆発の機構:ラベルの直積
時系列の総数は、各ラベルが取りうる値の数の 直積(デカルト積) で決まります。あるメトリクスのラベルが method(3種)・status(5種)・path(200種)だとすると、最悪ケースの時系列数は次のように掛け算で効きます。
3 × 5 × 200 = 3,000 系列
問題は、ここに 高基数ラベル を1つ足したときです。たとえば user_id(10万種)を加えると、
3 × 5 × 200 × 100,000 = 3億 系列
ラベルを1本足しただけで、時系列数が桁違いに膨れ上がりました。これが 基数爆発(cardinality explosion) です。各ラベルの値域が乗算で結合するため、増え方は加算ではなく 乗算的(実質的に指数的) になります。
user_id・request_id・email・session_id・full_url(クエリ文字列込み)・コンテナの pod_name/instance のような値の種類が無限に近い属性をラベルにすると、即座に爆発します。とくに「いつか役立つかも」でIDをラベル化するのは禁物です。
なぜ TSDB に重いのか:格納コストの内訳
Prometheus の時系列DB(TSDB)は、時系列1本ごとに独立した格納構造 を持ちます。だから本数の増加がそのままコストになります。コストは大きく3つに分かれます。
| コスト要素 | 何が起きるか | 基数との関係 |
|---|---|---|
| メモリ(head) | 直近データは head チャンクとして全時系列ぶんメモリ常駐 | 系列数に比例して常駐メモリが増える |
| 索引(index) | ラベル→系列の転置索引(posting list)を保持 | ラベル値の種類が増えるほど索引が肥大 |
| クエリ | PromQL は索引で系列を引いてから走査する | マッチする系列数が多いほど遅く重い |
ポイントは head(メモリ上の直近ブロック) です。Prometheus はスクレイプした最新データを、いったん時系列ごとのチャンクとしてメモリに載せます。サンプル1点のデータ量は圧縮で小さくても、「アクティブな時系列の本数」ぶんだけチャンクと索引エントリが常駐 するため、系列数が増えるとメモリが線形に膨らみます。数百万系列を超えたあたりからメモリ枯渇(OOM)が現実的な脅威になります。
デプロイのたびに pod_name や instance が変わると、古い時系列が役目を終え、新しい時系列が次々生まれます。これを チャーン(churn) と呼びます。瞬間の系列数は一定でも、索引には消えた系列のエントリが残り続け、保持期間内のユニーク系列総数が膨張します。「常時は軽いのにデプロイ後に重い」典型がこれです。
抑制の原理(1):集約で次元を落とす
最も効くのは、そもそも 不要なラベルを持たせない/集約で消す ことです。ダッシュボードやアラートで「ユーザー単位の内訳」が要らないなら、user_id はメトリクスに載せず、sum without(user_id) 的に次元を畳んでしまえば、直積の項が1つ消え、系列数が劇的に減ります。
設計原則はシンプルで、ラベルは「集計軸として実際に使う属性」だけにする ことです。可観測性の三本柱(/devops/observability/)で言えば、メトリクスは傾向把握、個別事例の追跡はログ・トレースの役割。役割分担を守るのが基数を抑える本質です。
抑制の原理(2):事前集約とサンプリング
次の層は 格納の前後で量を削る 手段です。
- recording rule(事前集約):頻用するクエリ結果を定期的に計算し、集約済みの新しい時系列として保存 する。生の高基数メトリクスは短い保持期間に留め、ダッシュボードは事前集約済みの低基数メトリクスを参照する。クエリ時の系列走査量を恒常的に下げられる。
- メトリクス・リラベル(drop/keep):スクレイプ時に
metric_relabel_configsで不要なラベルや系列を取り込む前に捨てる。爆発源を入口で止める最終防衛線。 - 集約サンプリング:エージェント層(OpenTelemetry Collector 等)で、生メトリクスを集約・間引きしてから送る。送信前に次元を落とすことで、TSDB に届く本数自体を減らす。
recording rule で集約するときは、残したいラベルを by() / without() で明示します。sum(rate(http_requests_total[5m])) by (status) のように集約軸を絞れば、path や instance の直積が消え、結果系列が激減します。「全ラベル込みで rate を取ってから集約」では、生の系列を全走査するので軽くなりません。
抑制の原理(3):exemplar でトレースへ逃がす
「集約すると個別の遅いリクエストが追えなくなるのでは」という懸念に答えるのが exemplar(エグザンプラ) です。exemplar は、集約されたメトリクス(とくにヒストグラム)のサンプル点に、代表的な1リクエストの trace_id を“添付”する 仕組みです。
# ヒストグラムのバケットに、その値を生んだ代表リクエストの trace_id を紐づける
http_request_duration_seconds_bucket{le="0.5"} 24054 # {trace_id="abc123"} 0.48
メトリクス本体は 低基数のまま(trace_id はラベルではなく exemplar として別枠で保持される)に保ちつつ、「このp99の山を作った実例はこのトレース」という橋渡しができます。つまり、基数を増やさずに、メトリクスから個別事例(トレース)へジャンプできる。集約で失った粒度を、トレース側で補う設計です。具体的な追跡は /devops/distributed-tracing/ に委ねられます。
高基数の情報は「ラベル(=系列を増やす)」ではなく「exemplar・ログ・トレース(=系列を増やさない)」に載せる。これが鉄則です。メトリクスは“何かおかしい”を安く広く、個別の“なぜ”はトレース/ログに逃がす。
まとめ
- 時系列数は メトリクス名 × 各ラベルの値域の直積 で決まり、高基数ラベルを1本足すだけで乗算的に膨らむ。これが基数爆発。
- Prometheus の TSDB は 系列ごとに head チャンクと索引 を持つため、本数増加が メモリ・索引・クエリ を同時に直撃する。チャーンによる索引肥大も見落とせない。
- 抑制は3層。(1) ラベル設計と集約で次元を落とす、(2) recording rule・リラベル・集約サンプリングで本数を削る、(3) exemplar で個別事例をトレースへ逃がす。
- 容量設計の観点は /devops/capacity-planning/、指標そのものの目標設計は /devops/sre-slo/ と併せて考えると、抑制の判断基準が定まります。
DevOps/インフラ Article
メトリクスの基数爆発とその抑制を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
Prometheus
比較で見る軸
難易度: advanced / カテゴリ: DevOps/インフラ / タグ数: 5
導入後に効く点
PrometheusのTSDBは時系列ごとにメモリ上のheadチャンクと索引を持つため、基数の増大はメモリ・索引・クエリ全体を直撃する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- DevOps/インフラ
- タグ数
- 5
判断チェックリスト
- 自社の用途が「Prometheus / メトリクス」に近いか確認する。
- 強みである「時系列数はメトリクス名とラベル値の直積で決まり、ラベルを掛け合わせるほど指数的に増える。これが基数爆発。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。