「監視はしているのに、障害のたびに原因探しで何時間もかかる」という悩みに答える考え方が、オブザーバビリティ (可観測性) です。この記事ではオブザーバビリティとは何か、監視とどう違うかを具体例で説明し、メトリクス・ログ・トレースと OpenTelemetry の役割を整理します。後半では多くの障害の現場になるデータベースについて、何を観測すればよいかを SQL つきで紹介します。
オブザーバビリティとは
オブザーバビリティ (observability、可観測性) は、システムの外に出てくるデータから、システムの中で何が起きているかを理解できる度合いのことです。OpenTelemetry の公式ドキュメントは、オブザーバビリティを「システムについて質問できるようにすることで、そのシステムを外から理解できるようにするもの」と説明しています。
ポイントは「質問できる」ことです。障害が起きたとき、「どの顧客で遅いのか」「どの API のどの SQL が遅いのか」「先週のリリースの前後で何が変わったのか」と、次々に問いが出てきます。オブザーバビリティが高いシステムでは、新しくコードを書いてデプロイしなくても、すでに集めているデータでこうした問いに答えられます。
オブザーバビリティはツールの名前ではなく、システムの性質です。ある製品を入れたらオブザーバビリティが手に入るわけではなく、どんなデータを、どんな粒度で、どう結びつけて残すかで決まります。
監視との違い
監視 (モニタリング) とオブザーバビリティの違いは、「問いをいつ決めるか」で整理すると分かりやすくなります。
| 観点 | 監視 | オブザーバビリティ |
|---|---|---|
| 問い | 事前に決めた問いに答える (CPU は 90% を超えたか、サイトは応答するか) | あとから立てた問いに答える (なぜこの顧客だけ遅いのか) |
| 得意なこと | 既知の異常にすばやく気づく | 想定していなかった異常の原因を探る |
| データ | 決めた指標の値 | 細かい粒度のイベント (リクエストごと、クエリごと) と、それをつなぐ情報 |
| 出力 | アラート、ダッシュボード | 調査の結果。そこから新しい監視が生まれることもある |
たとえば、注文 API が遅くなったとします。
監視だけの場合は、「注文 API の応答時間が 2 秒を超えた」というアラートが届きます。異常があることは分かります。ただ、原因を調べるには、サーバーにログインしてログを grep し、DB に入って実行中のクエリを見て、と手作業で証拠を集めることになります。
オブザーバビリティがある場合は、遅いリクエストのトレースを開くと、時間のうち 1.8 秒が特定の SQL で使われていると分かります。その SQL の統計を見ると、昨日のリリースのあとから実行回数が 10 倍になっていて、リリースで N+1 クエリ (一覧の件数ぶん SQL を繰り返す書き方) が入ったと見当がつきます。ここまでを、新しいコードを足さずに調べられます。
2 つは対立するものではありません。異常に気づくのは監視の仕事で、気づいたあとに「なぜ」を掘るのがオブザーバビリティの仕事です。Google の SRE 本も、監視は「何が壊れているか」と「なぜ壊れているか」の 2 つに答えるべきだとしています。前者が症状で、後者が原因です。オブザーバビリティは、後者に答えるためのデータを用意しておく考え方です。
なぜ今オブザーバビリティが言われるのか
サーバー 1 台にアプリと DB が乗っていた時代は、そのサーバーを見れば大体のことが分かりました。今はロードバランサー、複数のコンテナ、マネージドの DB、キュー、外部の API と、1 つのリクエストが多くの部品を通ります。部品が増えるほど障害の起き方の組み合わせが増え、すべてを事前に監視項目として決めておくことができなくなります。そのため、あとから問いを立てられるデータを残しておく考え方が重視されるようになりました。
メトリクス・ログ・トレース
オブザーバビリティのためのデータ (テレメトリ) として、よく 3 つが挙げられます。
| 種類 | 何か | 向いている問い | 弱いところ |
|---|---|---|---|
| メトリクス | 一定時間ごとに集計した数値 (1 分あたりのリクエスト数、エラー率、CPU 使用率) | 何かがおかしいか。いつからか | 集計した時点で細かい情報が消える。誰の・どのリクエストかは分からない |
| ログ | 時刻つきの出来事の記録 | そのとき何が起きたか | 量が多く、検索と保存に費用がかかる。部品をまたいだつながりが分かりにくい |
| トレース | 1 つのリクエストが通った経路と、各部品での所要時間 | どこで時間がかかったか、どこで失敗したか | アプリへの組み込みが必要。全件を残すと量が多い |
トレースは「スパン」の集まりです。スパンは「注文 API の処理」「在庫を確かめる SQL」「決済 API の呼び出し」のような 1 つの作業の単位で、開始時刻、所要時間、属性を持ちます。同じリクエストのスパンは共通のトレース ID でつながるので、どの部品で時間を使ったかが一目で分かります。
3 つを結びつける
3 つのデータは、別々に持つだけでは効果が薄く、行き来できると力を発揮します。そのための工夫が 2 つあります。
1 つは、ログにトレース ID を入れることです。ログを JSON のような構造化された形式で出し、項目の 1 つにトレース ID を入れておくと、トレースからそのリクエストのログへ、ログからトレースへと移れます。
{"time":"2026-11-01T10:42:13.512+09:00","level":"error","service":"order-api",
"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","msg":"payment timeout",
"order_id":81234,"duration_ms":5003}
もう 1 つは、属性の名前をそろえることです。サービス名、環境 (本番か検証か)、バージョンを、メトリクス・ログ・トレースで同じ名前で付けておくと、「バージョン 2.14 の本番だけ」と同じ条件で 3 つを絞り込めます。
カーディナリティとコスト
オブザーバビリティを進めると、データの量と費用の問題に必ず突き当たります。
メトリクスでは、ラベル (属性) に入る値の種類の数を「カーディナリティ」と呼びます。ユーザー ID や注文 ID のように種類が多い値をメトリクスのラベルに入れると、組み合わせの数だけ時系列が増え、保存の費用が急に上がります。種類の多い値は、メトリクスではなくトレースやログの属性に入れるのが基本です。
トレースは、全リクエストを残すと量が多くなるため、一部だけを残す「サンプリング」をよく使います。エラーになったものや遅かったものを優先して残す方法もあります。
OpenTelemetry とは
OpenTelemetry はトレース・メトリクス・ログを作り、送り、集めるためのオープンソースの仕組みです。CNCF (Cloud Native Computing Foundation) のプロジェクトで、特定のベンダーに依存しません。
OpenTelemetry 自体はデータを保存したり画面で見せたりする「バックエンド」ではありません。集めたデータはオープンソースのツールや商用の監視サービスなど、OpenTelemetry に対応した好きなバックエンドに送ります。アプリに組み込む部分を OpenTelemetry にそろえておけば、あとでバックエンドを乗り換えても、アプリのコードを書き直さずに済みます。これが OpenTelemetry を選ぶ一番の理由です。
主な構成要素は次の 4 つです。
| 構成要素 | 役割 |
|---|---|
| API | アプリがテレメトリを作るための、言語ごとの決まった呼び出し方 |
| SDK | API の実装。データの加工や送信を受け持つ |
| Collector | データを受け取り、加工し、バックエンドに送る中継役 |
| OTLP | テレメトリを送るための標準の通信の形式 |
多くの言語には、Web フレームワークや DB ドライバーの呼び出しを自動でスパンにする「自動計装」の仕組みがあります。アプリのコードをほとんど変えずに、まずトレースを取り始められます。
設定は SDK 共通の環境変数で行えます。たとえば次のように、サービス名、送り先、サンプリングの割合を指定します。
# サービス名 (トレースやメトリクスに service.name として付く)
export OTEL_SERVICE_NAME=order-api
# 送り先の Collector。既定のポートは gRPC が 4317、HTTP が 4318
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
# トレースを 10% だけ残す (親のスパンがあれば、その判断に従う)
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
自動計装の入れ方は言語ごとに違うので、OpenTelemetry の公式ドキュメントで使っている言語のページを確認してください。
データベースのスパンに付く情報
OpenTelemetry には、データの属性の名前を決めた「セマンティック規約」があります。データベースの呼び出しのスパンには、DB の種類 (db.system.name、たとえば postgresql や mysql)、操作 (db.operation.name、SELECT など)、対象のテーブル (db.collection.name)、SQL の文 (db.query.text) といった属性が決められています。
個人情報の扱いで知っておきたいのは、SQL の値の扱いです。規約では、パラメータを使っていない SQL の文は、値を ? などに置き換えて伏せない限り、既定では集めないことになっています。パラメータの値そのもの (db.query.parameter.<key>) は、個人情報を含みうるため、明示的に有効にしたときだけ集めます。メールアドレスや氏名が監視のデータに流れ込まないよう、この既定を変えるときは慎重に判断してください。
データベースの観点で何を観測するか
アプリの遅さや障害の原因は、データベースにあることがよくあります。それでも、オブザーバビリティの解説ではアプリのトレースが中心で、DB の中が「黒い箱」のまま残りがちです。DB については、次の 4 つの層で観測すると抜けが少なくなります。
| 層 | 何を見るか | 答えられる問い |
|---|---|---|
| 資源 | CPU、メモリ、ディスクの使用量と I/O、接続数 | DB が限界に近いか |
| クエリ | SQL ごとの実行回数、合計時間、平均時間、読んだ行数 | どの SQL が重いか。いつから増えたか |
| 待ち | ロック待ち、I/O 待ち、接続の待ち | SQL が何を待っているか |
| 業務データ | 注文数、決済の成否、ジョブの滞留、データの更新が止まっていないか | 利用者や事業に影響が出ているか |
資源の層は、クラウドの管理画面や監視ツールで多くの人がすでに見ています。足りなくなりがちなのは、残りの 3 つです。
クエリ: SQL ごとの統計を残す
トレースの DB スパンは、1 つのリクエストの中で SQL にどれだけ時間がかかったかを示します。一方で、DB 全体で見てどの SQL が負荷の大半を占めているかは、DB 側の統計を見るほうが確実です。
PostgreSQL では pg_stat_statements 拡張を使います。shared_preload_libraries に pg_stat_statements を加えて再起動し、CREATE EXTENSION pg_stat_statements; を実行すると、SQL の形ごと (値を除いたもの) の統計が集まります。
-- PostgreSQL: 合計の実行時間が長い SQL の上位 10 件
SELECT queryid,
calls,
round(total_exec_time::numeric, 1) AS total_ms,
round(mean_exec_time::numeric, 2) AS mean_ms,
rows,
left(query, 80) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
列名の total_exec_time と mean_exec_time は PostgreSQL 13 以降の名前です。12 以前では total_time と mean_time を使います。
MySQL では Performance Schema の events_statements_summary_by_digest に、SQL の形 (ダイジェスト) ごとの統計があります。時間の列はピコ秒 (1 兆分の 1 秒) なので、ミリ秒に直して見ます。
-- MySQL: 合計の実行時間が長い SQL の上位 10 件
SELECT SCHEMA_NAME,
COUNT_STAR AS calls,
ROUND(SUM_TIMER_WAIT / 1e9, 1) AS total_ms,
ROUND(AVG_TIMER_WAIT / 1e9, 2) AS avg_ms,
SUM_ROWS_EXAMINED AS rows_examined,
LEFT(DIGEST_TEXT, 80) AS query
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
これらの統計は DB を起動してからの累計です。「いつから増えたか」に答えるには、定期的に (たとえば 5 分ごとに) 値を記録して差を取る必要があります。ここが監視ツールに任せたい部分です。
1 回ずつの遅い SQL を記録したい場合は、スロークエリログを使います。PostgreSQL では log_min_duration_statement (既定は -1 で無効)、MySQL では slow_query_log (既定は無効) と long_query_time (既定は 10 秒) で設定します。詳しくは PostgreSQL のスロークエリの調べ方 と MySQL のスロークエリの調べ方 で説明しています。
待ち: SQL が何を待っているか
SQL が遅いとき、その SQL 自体が重いとは限りません。ほかのトランザクションのロックを待っていることもあります。PostgreSQL では pg_stat_activity の wait_event_type と wait_event で、各セッションがいま何を待っているかが分かります。これを数えると、DB 全体が何に詰まっているかが見えます。
-- PostgreSQL: 実行中のセッションが何を待っているかを数える
SELECT coalesce(wait_event_type, 'CPU (待ちなし)') AS wait_type,
wait_event,
count(*) AS sessions
FROM pg_stat_activity
WHERE state = 'active'
AND backend_type = 'client backend'
GROUP BY 1, 2
ORDER BY sessions DESC;
Lock が多ければロックの競合、IO が多ければディスクの読み書き、Client ならアプリ側の受け取りを待っています。wait_event_type が空のセッションは待たずに処理を進めている状態です。この結果も一度見るだけでなく、定期的に記録しておくと、障害のときに「いつからロック待ちが増えたか」が分かります。ロック待ちの詳しい調べ方は ロック待ちの原因と対処 にまとめています。
業務データ: 利用者に影響が出ているか
資源もクエリも正常なのに、事業としては問題が起きていることがあります。決済代行の障害で決済だけが失敗している、取り込みのバッチが止まってデータが昨日から更新されていない、特定の機能の利用だけが急に減っている、といった状態です。こうした異常はアプリのエラー率や DB の資源の数字には出てこないことがあります。
業務データは、テーブルを直接数えるのが確実です。
-- PostgreSQL: 直近 1 時間の決済の状態ごとの件数と、未処理ジョブの最も古いもの
SELECT status, count(*)
FROM payments
WHERE created_at >= now() - interval '1 hour'
GROUP BY status;
SELECT count(*) AS pending_jobs,
min(created_at) AS oldest_pending
FROM jobs
WHERE status = 'pending';
テーブル名と列名は自社のものに置き換えてください。決済の失敗の割合、未処理のジョブの件数と最も古いものの時刻は、利用者への影響に近い数字です。SRE で使う SLI (サービスの水準を表す指標) にもそのまま使えます。SLI については SRE とは で説明しています。
少人数のチームはどこから始めるか
オブザーバビリティの解説は大きな組織を前提にしたものが多いですが、2〜5 人のチームでも順番を決めれば少しずつ進められます。
- 業務データの数字を 2〜3 個決めて、定期的に記録する。注文数、決済の成否、ジョブの滞留など、止まったら一番困るものから選びます。利用者への影響に一番近いデータです
- DB のクエリの統計を有効にする。
pg_stat_statementsや Performance Schema を有効にし、重い SQL の上位を週に一度見る習慣をつけます - ログを構造化する。JSON で出し、サービス名、環境、リクエスト ID を必ず入れます
- OpenTelemetry の自動計装でトレースを取る。最も大事な API から始め、サンプリングで量を抑えます。ログにトレース ID を入れて、行き来できるようにします
- 障害のたびに「答えられなかった問い」を記録する。ポストモーテムで「何があれば早く原因が分かったか」を挙げ、そのデータを足していきます
5 番目が一番大事です。オブザーバビリティは最初から完璧な設計を目指すより、実際の障害で困った問いに答えられるデータを足していく方が、無駄な費用をかけずに育てられます。振り返りの進め方は ポストモーテムとは で説明しています。
データベースの観測を続けるには
この記事で紹介したクエリの統計、待ちの集計、業務データの SQL は、cron で定期的に実行して結果を表に残すと、あとから「いつから変わったか」を調べられるようになります。監視ツールにメトリクスとして送り、ダッシュボードとアラートを作る方法もあります。DB の監視全体の考え方は データベース監視とは にまとめています。
業務データの部分は AI に任せる方法もあります。Scandock は、データベースの接続文字列を登録すると、AI がテーブルの構成と実データを読み、注文数や決済の成否、バッチ・ジョブの滞留のような事業の中心にある数字を監視項目にして、取得の間隔も決めます。異常と判断すると、読み取り専用のクエリで関連するデータを調べ、原因・影響・根拠・対処案をつけて通知し、実行したクエリと結果を残します。顧客の名前やメールアドレスなどの個人情報は伏せた状態で AI に渡します。
参考
- Observability primer (OpenTelemetry)
- What is OpenTelemetry? (OpenTelemetry)
- Semantic conventions for database client spans (OpenTelemetry)
- General SDK Configuration (OpenTelemetry)
- OTLP Exporter Configuration (OpenTelemetry)
- Monitoring Distributed Systems (Site Reliability Engineering, Google)
- PostgreSQL: pg_stat_statements
- PostgreSQL: The Cumulative Statistics System (pg_stat_activity)
- PostgreSQL: Error Reporting and Logging (log_min_duration_statement)
- MySQL 8.4 Reference Manual: Statement Summary Tables
- MySQL 8.4 Reference Manual: Performance Schema Event Timing
- MySQL 8.4 Reference Manual: The Slow Query Log