PromQLの「正規表現」は諸刃の剣:数百万の時系列データを一瞬で殺すアンチパターンと最適解
Prometheusを運用していると、誰もが一度は「特定のラベルを柔軟に拾いたい」という誘惑に駆られ、安易に正規表現(`=~`)を叩き込む。しかし、その「便利さ」の裏側で、PrometheusのCPUは悲鳴を上げ、クエリのレスポンスは数秒単位で遅延し、アラート通知すら届かない死の淵へとシステムを追い込んでいる可能性がある。
今日は、オブザーバビリティの最前線で戦う諸君に、「正規表現によるパフォーマンスの崩壊」のメカニズムと、それを覆すプロの書き換え術を授ける。
—
1. なぜ正規表現(=~)はPrometheusを殺すのか?
Prometheusのデータモデルは、ラベル名と値のペアでインデックス化されている。クエリが実行される際、Prometheusはインデックスを使って効率よく時系列データを絞り込む。
しかし、正規表現(`=~`)が使われた瞬間、このインデックスの恩恵は一部失われる。
- メカニズム: 正規表現が指定されると、Prometheusは該当する全ラベルセットを走査し、各値に対して正規表現エンジンのマッチング処理を逐次実行する。
- コストの正体: ラベルのカーディナリティ(値の種類数)が数万〜数十万規模になると、この「全走査+正規表現評価」がCPUを強烈に浪費する。特に`.`のようなワイルドカードが先頭にあると、インデックスを引くことができず、フルスキャンに近い負荷が発生する。
—
2. 現場でよく見る「死を招く」アンチパターン
以下のクエリは、いずれも地雷である。
① 冒頭ワイルドカードの罪
アンチパターン: 処理負荷が最大になる
http_requests_total{job=~”.-service”}
`.`(任意の一文字)と“(0回以上の繰り返し)が先頭にあると、インデックスを一切活用できない。もし`job`ラベルに大量のメトリクスが存在する場合、クエリはタイムアウト寸前までCPUを焼き尽くす。
② 不必要な部分一致
アンチパターン: 意図せず全走査
container_memory_usage_bytes{container=~”.nginx.”}
これも同様。`nginx`が含まれるか判定するために、メモリ上のすべてのコンテナ名を正規表現エンジンに通している。
—
3. 高速化のための「完全一致・複数指定」リファクタリング
正規表現から脱却し、「インデックスを活用する」書き方に変えるだけで、クエリの実行時間はミリ秒単位まで短縮できる。
A. 完全一致への置換(最速)
正規表現を使う必要がないなら、即座に `=` を使うこと。これだけでインデックス直接参照になる。
B. `|` (OR) による複数指定(推奨)
正規表現をどうしても使う必要がある場合、ワイルドカードではなく「具体的な値の列挙」に書き換える。
Before: 激重
http_requests_total{job=~”api-service|auth-service|order-service”}
After: 完全一致のOR結合(正規表現ではないと判定される場合が多い)
Prometheusは、| で区切られた完全一致リストをインデックス検索で処理できる
http_requests_total{job=~”^(api-service|auth-service|order-service)$”}
※ Prometheusのクエリエンジンは、`^`と`$`で囲まれた完全一致のORリストを、インデックス検索として最適化する能力を持っている。
—
4. テックリードが教える「Prometheus運用」の神髄
ただクエリを直すだけでは不十分だ。チーム全体の生産性を引き上げるために、以下の環境構築を推奨する。
① VS Code神プラグイン:PromQL Language Support
[PromQL Language Support](https://marketplace.visualstudio.com/items?itemName=Prometheus.vscode-prometheus) を導入せよ。クエリの文法チェックはもちろん、インテリセンスが効くようになるため、タイポによる計算ミスや不適切なラベル指定を未然に防げる。
② 共有ルール:Recording Rulesの徹底活用
重い正規表現クエリをダッシュボードやアラートで直接叩くのは禁止だ。Recording Rulesとして事前計算し、軽量な結果セットを保存しておくのが鉄則である。
prometheus.rules.yml
groups:
- name: service_metrics
rules:
- record: job:http_requests_total:sum
expr: sum by (job) (rate(http_requests_total[5m]))
# これにより、ダッシュボードは正規表現なしの軽量なクエリを参照する
③ 隠れた時短テクニック:`promtool` をCIに組み込む
設定ファイルやクエリの検証には `promtool` を使え。
設定ファイルの文法チェック(デプロイ前の自動テストで必須)
promtool check config prometheus.yml
クエリの事前チェック(デッドコードや重い正規表現の検知に)
promtool test rules my_rules.yml
—
5. まとめ:オブザーバビリティの品格
「動くクエリ」を書くのはジュニアエンジニアだ。「速く、かつ低負荷なクエリ」を書くのがアーキテクトだ。
1. `=~` は最終手段: インデックスを殺す正規表現を排除し、完全一致(`=`)を極めよ。
2. インデックス検索を意識する: `^` と `$` で囲んだ完全一致リストを利用せよ。
3. Recording Rulesを信仰する: 重い計算はバックグラウンドで終わらせておく。
Prometheusは正直なツールだ。君が書いたクエリの負荷が、そのままサーバーのコストとして跳ね返ってくる。今日からクエリの裏側にいる「CPU」の気持ちになって、コードを書いてみてほしい。それが、卓越した監視エンジニアへの唯一の道だ。