profiling-statement-fingerprints について
CockroachDB の集約統計から、過去の SQL ステートメントフィンガープリントを分析する運用スキルです。DB Console にアクセスせず SQL を使い、性能、リソース使用量、実行計画、エラーの傾向を調べられます。
主な機能
- 01時間範囲を指定した指紋分析
- 02レイテンシと実行回数の確認
- 03CPU・競合・アドミッション待ちの分析
- 04実行計画の差異と全件スキャンの確認
- 05メモリスピルと失敗の検出
活用シーン
- 01数時間から数日間の遅い指紋の特定
- 02クエリ性能の悪化調査
- 03競合や CPU 使用量の多いクエリの特定
- 04全件スキャンとインデックス推奨の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cockroachlabs/ cockroachdb-skills --skill profiling-statement-fingerprintsインストール後、Claude Code で/profiling-statement-fingerprintsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/json-field-reference.mdmetrics-and-units.mdsql-query-variations.md詳細
ステートメントフィンガープリントのプロファイリング
集約された SQL 統計情報を使用して、過去のステートメントパフォーマンスのパターンを分析し、低速、リソース消費の多い、またはエラーが発生しやすいクエリフィンガープリントを特定します。crdb_internal.statement_statistics を使用して、レイテンシ、CPU、競合、アドミッション遅延、失敗率を時間範囲ごとに分析します。すべて SQL で実行でき、DB Console へのアクセスは不要です。
triaging-live-sql-activity の補完: このスキルは過去のパターンを分析します。現在実行中のクエリをただちにトリアージするには、triaging-live-sql-activity を参照してください。
このスキルを使用する場合
- 過去数時間、数日、数週間で最も遅いステートメントフィンガープリントを特定する
- CPU 消費量、競合、アドミッション待機時間が多いクエリを見つける
- パフォーマンスの低下やプランの変更を調査する
- インデックスの推奨事項を使って、フルテーブルスキャンや不足しているインデックスを特定する
- アプリケーションまたはデータベース別にリソース消費を分析する
- DB Console にアクセスせず、SQL のみで過去のデータを分析する
インシデントへの即時対応: 現在実行中のクエリをトリアージし、暴走した処理をキャンセルするには、triaging-live-sql-activity を使用してください。 トランザクションレベルの分析: トランザクションの境界でリトライのパターン、コミットレイテンシ、ステートメントの構成を分析するには、profiling-transaction-fingerprints を使用してください。 バックグラウンドジョブの監視: ステートメント統計から除外される、長時間実行されるスキーマ変更や自動ジョブには、monitoring-background-jobs を使用してください。
前提条件
- CockroachDB クラスターへの SQL 接続
- クラスター全体を表示するには、
VIEWACTIVITYまたはVIEWACTIVITYREDACTEDクラスター権限が必要です - ステートメント統計の収集が有効であること(デフォルト):
sql.stats.automatic_collection.enabled = true
収集ステータスの確認:
SHOW CLUSTER SETTING sql.stats.automatic_collection.enabled; -- Should return: true
RBAC の設定については、triaging-live-sql-activity の権限リファレンスを参照してください(必要な権限は同じです)。
主要な概念
ステートメントフィンガープリントと実行中のクエリ
ステートメントフィンガープリント: 定数をパラメーター化した正規化済み SQL パターンです(例:SELECT * FROM users WHERE id = $1 と SELECT * FROM users WHERE id = 123)
主な違い:
- 時間範囲: 過去の時間単位のバケットと、リアルタイムの現在の状態
- 粒度: 集約されたパターンの統計情報と、個々の実行インスタンス
時系列のバケット分け
aggregated_ts: UTC の時間単位のバケットです(例:2026-02-21 14:00:00 は 14:00~14:59 の実行を表します)
データ保持: 時間ではなく行数に上限が設定されています。sql.stats.persisted_rows.max(デフォルトは 1,000,000)は、永続化されるステートメントとトランザクションの行数の上限です。上限に達すると、古いバケットは統合されます。実際の時間範囲はワークロードの多様性によって異なります。
ベストプラクティス: 常に時間範囲で絞り込みます:WHERE aggregated_ts > now() - INTERVAL '24 hours'
集約メトリクスとサンプリングメトリクス
| メトリクスのカテゴリ | JSON パス | 範囲 | 用途 |
|---|---|---|---|
| 集約 | statistics.statistics.* | すべての実行 | レイテンシ、行数、実行回数 |
| サンプリング | statistics.execution_statistics.* | sql.txn_stats.sample_rate(デフォルトは 0.01)に基づく確率的サンプル | CPU、競合、アドミッション待機時間、メモリ/ディスク |
重要: サンプリングメトリクスが存在することを常に確認します:WHERE (statistics->'execution_statistics'->>'cnt') IS NOT NULL
JSON フィールドの抽出
演算子:
->:JSON オブジェクトを抽出します(JSON を返します)->>:テキストとして抽出します(テキストを返します)::TYPE:特定の型にキャストします
例:
metadata->>'db' -- Database name
(statistics->'statistics'->>'cnt')::INT -- Execution count
(statistics->'statistics'->'runLat'->>'mean')::FLOAT8 -- Mean latency (seconds)
(statistics->'execution_statistics'->'cpuSQLNanos'->>'mean')::FLOAT8 / 1e9 -- CPU (convert nanos to seconds)
単位: レイテンシ=秒、CPU/アドミッション=ナノ秒(÷ 1e9)、メモリ/ディスク=バイト(MB にするには ÷ 1048576)
スキーマ全体については、JSON フィールドリファレンスを参照してください。
主な診断クエリ
クエリ 1:実行レイテンシの平均値が高いステートメント
SELECT
fingerprint_id,
metadata->>'db' AS database,
metadata->>'query' AS query_text,
(statistics->'statistics'->>'cnt')::INT AS execution_count,
(statistics->'statistics'->'runLat'->>'mean')::FLOAT8 AS mean_run_lat_seconds,
(statistics->'statistics'->'runLat'->>'max')::FLOAT8 AS max_run_lat_seconds,
(metadata->>'fullScan')::BOOL AS full_scan,
metadata->'index_recommendations' AS index_recommendations,
aggregated_ts
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '24 hours'
AND (statistics->'statistics'->'runLat'->>'mean')::FLOAT8 > 1.0 -- > 1 second mean latency
ORDER BY (statistics->'statistics'->'runLat'->>'mean')::FLOAT8 DESC
LIMIT 20;
注目点: 最も遅いクエリです。最適化の機会を探るため、full_scan と index_recommendations を確認してください。
クエリ 2:アドミッションコントロールの影響
SELECT
fingerprint_id,
metadata->>'db' AS database,
metadata->>'query' AS query_text,
(statistics->'statistics'->>'cnt')::INT AS execution_count,
(statistics->'execution_statistics'->'admissionWaitTime'->>'mean')::FLOAT8 / 1e9 AS mean_admission_wait_seconds,
(statistics->'statistics'->'runLat'->>'mean')::FLOAT8 AS mean_run_lat_seconds,
aggregated_ts
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '24 hours'
AND (statistics->'execution_statistics'->>'cnt') IS NOT NULL
AND (statistics->'execution_statistics'->'admissionWaitTime'->>'mean')::FLOAT8 > 0
ORDER BY (statistics->'execution_statistics'->'admissionWaitTime'->>'mean')::FLOAT8 DESC
LIMIT 20;
解釈: アドミッション待機時間が長い場合、クラスターのリソース(CPU、メモリー、I/O)が上限に達しています。比率が 1.0 を超える(待機時間が実行時間を上回る)場合は、深刻なキューイングが発生しています。
クエリ 3:プランハッシュの多様性
SELECT
fingerprint_id,
metadata->>'db' AS database,
metadata->>'query' AS query_text,
COUNT(DISTINCT plan_hash) AS distinct_plan_count,
array_agg(DISTINCT plan_hash ORDER BY plan_hash) AS plan_hashes,
SUM((statistics->'statistics'->>'cnt')::INT) AS total_executions
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '7 days'
GROUP BY fingerprint_id, metadata->>'db', metadata->>'query'
HAVING COUNT(DISTINCT plan_hash) > 1
ORDER BY COUNT(DISTINCT plan_hash) DESC, SUM((statistics->'statistics'->>'cnt')::INT) DESC
LIMIT 20;
解釈: 複数のプランは、スキーマ変更、統計情報の更新、またはルーティングの変更による不安定さを示しています。プランによってパフォーマンスが大きく変動する可能性があります。
クエリ 4:競合の多いステートメント
SELECT
fingerprint_id,
metadata->>'db' AS database,
metadata->>'app' AS application,
substring(metadata->>'query', 1, 150) AS query_preview,
(statistics->'statistics'->>'cnt')::INT AS execution_count,
(statistics->'execution_statistics'->'contentionTime'->>'mean')::FLOAT8 / 1e9 AS mean_contention_seconds,
ROUND(
((statistics->'execution_statistics'->'contentionTime'->>'mean')::FLOAT8 / 1e9) /
NULLIF((statistics->'statistics'->'runLat'->>'mean')::FLOAT8, 0) * 100, 2
) AS contention_pct_of_runtime,
aggregated_ts
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '24 hours'
AND (statistics->'execution_statistics'->>'cnt') IS NOT NULL
AND (statistics->'execution_statistics'->'contentionTime'->>'mean')::FLOAT8 > 0
ORDER BY (statistics->'execution_statistics'->'contentionTime'->>'mean')::FLOAT8 DESC
LIMIT 20;
解釈: 競合率が 20% を超える場合は、トランザクションの競合やホット行へのアクセスが発生しています。バッチ処理、トランザクション境界の変更、またはスキーマの再設計で対処してください。
クエリ 5:CPU 使用量の多いクエリ
SELECT
fingerprint_id,
metadata->>'db' AS database,
substring(metadata->>'query', 1, 150) AS query_preview,
(statistics->'execution_statistics'->'cpuSQLNanos'->>'mean')::FLOAT8 / 1e9 AS mean_cpu_seconds,
(statistics->'statistics'->>'cnt')::INT AS total_executions,
ROUND(
((statistics->'execution_statistics'->'cpuSQLNanos'->>'mean')::FLOAT8 / 1e9) *
(statistics->'statistics'->>'cnt')::INT, 2
) AS estimated_total_cpu_seconds,
(metadata->>'fullScan')::BOOL AS full_scan,
aggregated_ts
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '24 hours'
AND (statistics->'execution_statistics'->>'cnt') IS NOT NULL
AND (statistics->'execution_statistics'->'cpuSQLNanos'->>'mean')::FLOAT8 > 0
ORDER BY estimated_total_cpu_seconds DESC
LIMIT 20;
注目点: estimated_total_cpu_seconds はクラスターへの影響を示します。CPU 平均使用量が高い場合、多くは full_scan = true と相関しています。
クエリ 6:メモリーおよびディスクへのスピルの検出
SELECT
fingerprint_id,
metadata->>'db' AS database,
substring(metadata->>'query', 1, 150) AS query_preview,
(statistics->'execution_statistics'->'maxMemUsage'->>'mean')::FLOAT8 / 1048576 AS mean_mem_mb,
(statistics->'execution_statistics'->'maxMemUsage'->>'max')::FLOAT8 / 1048576 AS max_mem_mb,
(statistics->'execution_statistics'->'maxDiskUsage'->>'mean')::FLOAT8 / 1048576 AS mean_disk_mb,
(statistics->'execution_statistics'->'maxDiskUsage'->>'max')::FLOAT8 / 1048576 AS max_disk_mb,
metadata->>'stmtType' AS statement_type,
aggregated_ts
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '24 hours'
AND (statistics->'execution_statistics'->>'cnt') IS NOT NULL
AND (statistics->'execution_statistics'->'maxDiskUsage'->>'mean')::FLOAT8 > 0 -- Has disk spills
ORDER BY (statistics->'execution_statistics'->'maxDiskUsage'->>'mean')::FLOAT8 DESC
LIMIT 20;
解釈: ディスク使用量が 0 を超える場合は、メモリーからのスピルが発生しています(メモリー内処理より約 100~1000 倍遅くなります)。大規模な集約、ソート、ハッシュ結合でよく発生します。インデックスの追加、または sql.distsql.temp_storage.workmem の増加で対処してください。
クエリ 7:エラーが発生しやすいステートメント
SELECT
fingerprint_id,
metadata->>'db' AS database,
substring(metadata->>'query', 1, 150) AS query_preview,
(statistics->'statistics'->>'cnt')::INT AS total_executions,
COALESCE((statistics->'statistics'->>'failureCount')::INT, 0) AS failure_count,
ROUND(
COALESCE((statistics->'statistics'->>'failureCount')::INT, 0)::NUMERIC /
NULLIF((statistics->'statistics'->>'cnt')::INT, 0) * 100, 2
) AS failure_rate_pct,
aggregated_ts
FROM crdb_internal.statement_statistics
WHERE aggregated_ts > now() - INTERVAL '24 hours'
AND (statistics->'statistics'->>'cnt')::INT > 10
AND COALESCE((statistics->'statistics'->>'failureCount')::INT, 0) > 0
ORDER BY failure_rate_pct DESC, failure_count DESC
LIMIT 20;
一般的な原因: 制約違反、クエリのタイムアウト、トランザクションの再試行エラー(40001)、権限拒否。
一般的なワークフロー
ワークフロー 1:遅延の調査
- 遅いフィンガープリントを特定する: クエリ 1 を 24 時間の期間で実行し、
mean_run_lat_seconds > 5かつ実行回数が多いものに注目します - 全件スキャンを確認する:
full_scan = trueで絞り込み、index_recommendationsを確認します - アプリケーションと関連付ける:
metadata->>'app'でグループ化し、該当するパターンについてチームに連絡します - 実行中のアクティビティと照合する: 継続中の場合は、triaging-live-sql-activity を使って暴走したクエリをキャンセルします
ワークフロー 2:競合の分析
- 競合の多いステートメントを見つける: クエリ 4 を実行し、
contention_pct_of_runtime > 20%に注目します - プランの安定性を確認する: 競合しているフィンガープリントに対してクエリ 3 を実行します(プランの変更はロック順序に影響します)
- 対処する: 操作をバッチ化し、
SELECT FOR UPDATEを使用し、競合の多いテーブルをパーティション分割し、スキーマを非正規化します
ワークフロー 3:アドミッションコントロールのデバッグ
- アドミッション待機を特定する: クエリ 2 を実行し、待機比率を計算します
- CPU と関連付ける: 同じ期間でクエリ 5 を実行し、フィンガープリント ID を照合します
- 時間パターンを分析する:
aggregated_tsでグループ化し、ピーク時間帯を特定します - トリアージする: 短期的にはバッチジョブを分散させます。長期的にはキャパシティを増強し、クエリを最適化します
ワークフロー 4:メモリースピルの調査
- スピルしているステートメントを見つける: クエリ 6 を実行し、
max_disk_mb > 100に注目します - パターンを分析する: 大規模な
GROUP BY、ORDER BY、ハッシュ結合を特定します - 対処する: インデックスを追加し、慎重に workmem を増やし、クエリを書き換え、マテリアライズドビューを使用します
安全性に関する考慮事項
読み取り専用操作: すべてのクエリは、本番環境での使用が承認された crdb_internal.statement_statistics に対する SELECT ステートメントです。
パフォーマンスへの影響:
| 考慮事項 | 影響 | 緩和策 |
|---|---|---|
| 大きなテーブル | ステートメントの多様性が高く、行数が多い | 常に時間で絞り込み、LIMIT を使用する |
| JSON の解析 | CPU オーバーヘッド | 具体的な時間範囲を使い、短い間隔での繰り返しを避ける |
| 広い時間範囲 | 7 日間のクエリでは行数が増える | 既定では 24 時間とし、必要な場合にのみ範囲を広げる |
プライバシー: マルチテナント環境では VIEWACTIVITYREDACTED を使用してクエリ定数を秘匿してください。
トラブルシューティング
| 問題 | 原因 | 対処方法 |
|---|---|---|
| 結果が空 | データがない、または統計情報の収集が無効 | sql.stats.automatic_collection.enabled = true を確認してください |
column does not exist | JSON フィールドのタイプミス、またはバージョンの不一致 | フィールド名を確認し、CockroachDB のバージョンを確認してください |
| サンプリングされたメトリクスが NULL | メトリクスがバケット内でサンプリングされていない | 次の条件でフィルターします:WHERE (statistics->'execution_statistics'->>'cnt') IS NOT NULL |
クエリテキストが <hidden> と表示される | VIEWACTIVITYREDACTED を使用している | 想定どおりです。権限がある場合は VIEWACTIVITY を使用してください |
| "invalid input syntax for type json" | JSON パスの形式が正しくない | 演算子を確認してください:JSON には ->、テキストには ->> を使用します |
| クエリが非常に遅い | テーブルが大きく、時間フィルターがない | 必ず時間範囲と LIMIT を追加してください |
index_recommendations が空 | 推奨事項がない、または最適な状態 | インデックスが存在する場合は正常です |
主な考慮事項
- 時間範囲: デフォルトは 24h です。傾向を確認するには 7d に拡大します
- サンプリングされたメトリクス: すべての実行が記録されるわけではありません。サンプルサイズ(
cnt)を確認してください - JSON の安全性: NULL を防御的にチェックし、型変換エラーに対処してください
- プライバシー: 本番環境では VIEWACTIVITYREDACTED を使用してください
- パフォーマンス: 必ず時間フィルターと LIMIT を含めてください
- リアルタイムのトリアージを補完: 全体を把握するため、併用してください(履歴データ+リアルタイム)
- データ保持: TTL ではなく、行数上限
sql.stats.persisted_rows.max(デフォルトは 1,000,000)によって制限されます。有効な時間範囲はワークロードの多様性によって変わります - 実行計画の不安定性: 複数のプランハッシュは、オプティマイザーまたはスキーマの変更を示します
参考資料
スキルの参考資料:
- JSON フィールドのスキーマと抽出
- メトリクスのカタログと単位
- SQL クエリのバリエーション
- RBAC と権限(triaging-live-sql-activity と共用)
CockroachDB 公式ドキュメント:
関連スキル:
- triaging-live-sql-activity - 現在実行中のクエリを迅速にトリアージする場合
- profiling-transaction-fingerprints - リトライパターンやコミットレイテンシを含む、トランザクションレベルの分析を行う場合