DataBase  /  PostgreSQL  /  レポート取得ガイド

pg_stat_statements による取得とセクション定義

拡張モジュール pg_stat_statements を使って、Oracle AWR の SQL Statistics セクション相当の 重い SQL・累積実行時間・呼び出し回数・I/O 統計・WAL 統計を取得する方法と、 各列のセクション定義を詳しく解説します。

1. pg_stat_statements の有効化

postgresql.conf — 設定追加
# shared_preload_libraries に追加(再起動が必要)
shared_preload_libraries = 'pg_stat_statements'

# オプション設定
pg_stat_statements.max = 10000       # 追跡する SQL の最大数(デフォルト:5000)
pg_stat_statements.track = all       # all=全クエリ, top=トップレベルのみ, none=無効
pg_stat_statements.track_utility = on  # COPY・VACUUM などのユーティリティも追跡
pg_stat_statements.save = on          # 再起動後も統計を保持
SQL — 拡張インストールと確認
-- 拡張をインストール(superuser 権限が必要)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- インストール確認
SELECT * FROM pg_extension WHERE extname = 'pg_stat_statements';

-- バージョン確認
SELECT pg_stat_statements_info();
⚠️ shared_preload_libraries の変更は再起動が必要
postgresql.conf に追加後、PostgreSQL を再起動してください。 CREATE EXTENSION は再起動後に実行します。 既に他のライブラリが設定されている場合はカンマ区切りで追記します。

2. 累積実行時間の多い SQL(AWR: SQL ordered by Elapsed Time 相当)

SQL — 累積実行時間 Top 20(Oracle AWR SQL Statistics 相当)
SELECT
    ROUND(total_exec_time::numeric, 2)                                AS total_exec_ms,
    calls,
    ROUND((total_exec_time / calls)::numeric, 2)                      AS avg_exec_ms,
    ROUND(min_exec_time::numeric, 2)                                  AS min_exec_ms,
    ROUND(max_exec_time::numeric, 2)                                  AS max_exec_ms,
    ROUND(stddev_exec_time::numeric, 2)                               AS stddev_exec_ms,
    rows,
    ROUND((100 * total_exec_time / SUM(total_exec_time) OVER())::numeric, 2) AS pct_total,
    left(query, 100)                                                  AS query_preview
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

3. 実行回数の多い SQL(AWR: SQL ordered by Executions 相当)

SQL — 実行回数 Top 20
SELECT
    calls,
    ROUND(total_exec_time::numeric, 2)               AS total_exec_ms,
    ROUND((total_exec_time / calls)::numeric, 2)     AS avg_exec_ms,
    rows,
    ROUND((rows::numeric / calls), 2)                AS avg_rows,
    left(query, 100)                                 AS query_preview
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 20;

4. I/O の多い SQL(AWR: SQL ordered by Gets 相当)

SQL — 共有バッファ読み込みの多い SQL Top 20
SELECT
    calls,
    shared_blks_hit + shared_blks_read              AS total_blks,
    shared_blks_hit,
    shared_blks_read,
    ROUND(shared_blks_hit::numeric /
        NULLIF(shared_blks_hit + shared_blks_read, 0) * 100, 2) AS hit_ratio,
    shared_blks_dirtied,
    shared_blks_written,
    local_blks_hit + local_blks_read                AS local_blks_total,
    temp_blks_read,
    temp_blks_written,
    left(query, 100)                                AS query_preview
FROM pg_stat_statements
ORDER BY shared_blks_read DESC
LIMIT 20;

5. pg_stat_statements 全列定義

識別情報

列名説明Oracle AWR 対応
userid SQL を実行したユーザーの OID。pg_roles.oid と結合で名前に変換。 Parsing User ID
dbid SQL が実行されたデータベースの OID。
toplevel (PostgreSQL 14+)トップレベルの SQL として実行されたか。false はストアドプロシージャ内などからの呼び出し。
queryid 正規化後のクエリテキストのハッシュ値。同一パターンの SQL を識別するキー。 SQL_ID
query 正規化されたクエリテキスト(リテラル値は $1/$2... に置換)。 SQL_TEXT

実行統計

列名説明Oracle AWR 対応
calls 累積実行回数。 Executions
total_exec_time 累積実行時間(ms)。プランニング時間は含まない。Oracle の Elapsed Time に相当。 Elapsed Time
min_exec_time 最小実行時間(ms)。
max_exec_time 最大実行時間(ms)。スパイクの検出に使う。
mean_exec_time 平均実行時間(ms)。 Elapsed Time / Executions
stddev_exec_time 実行時間の標準偏差(ms)。大きい場合は実行時間が不安定。
total_plan_time (PostgreSQL 13+)累積プランニング時間(ms)。高い場合は実行計画のキャッシュを検討。
rows 累積返却行数(SELECT)または影響行数(INSERT/UPDATE/DELETE)。 Rows Processed

ブロック I/O 統計

列名説明Oracle AWR 対応
shared_blks_hit 共有バッファキャッシュからヒットした累積ブロック数。 Buffer Gets(Logical Reads)
shared_blks_read ディスクから読み込んだ累積ブロック数(キャッシュミス)。 Physical Reads
shared_blks_dirtied ダーティにした累積ブロック数(更新が発生したブロック)。
shared_blks_written 共有バッファから書き込んだ累積ブロック数。 Physical Writes
local_blks_hit ローカルバッファ(一時テーブル用)からヒットした累積ブロック数。
local_blks_read ローカルバッファのキャッシュミスによるディスク読み込み数。
temp_blks_read 一時ファイルから読み込んだ累積ブロック数(ソート・ハッシュ結合の work_mem 超過)。 Sorts (disk)
temp_blks_written 一時ファイルに書き込んだ累積ブロック数。

I/O 時間統計(track_io_timing = on が必要)

列名説明Oracle AWR 対応
blk_read_time ディスク読み込みに費やした累積時間(ms)。 db file sequential / scattered read
blk_write_time ディスク書き込みに費やした累積時間(ms)。 db file parallel write
temp_blk_read_time (PostgreSQL 15+)一時ファイルの読み込み時間(ms)。
temp_blk_write_time (PostgreSQL 15+)一時ファイルへの書き込み時間(ms)。

WAL 統計(PostgreSQL 13+)

列名説明Oracle AWR 対応
wal_records 生成した WAL レコード数。更新系 SQL の Write 量の指標。 Redo Size
wal_fpi 生成した WAL フルページイメージ数(チェックポイント後の初回更新時)。
wal_bytes 生成した WAL の合計バイト数。 Redo Writes(bytes)
💡 track_io_timing の有効化
blk_read_time / blk_write_time を取得するには postgresql.conftrack_io_timing = on が必要です。 OS のタイマー精度に依存するためオーバーヘッドがありますが、 I/O ボトルネックの特定には非常に有効です。

6. スナップショット差分による期間分析

pg_stat_statements は累積値であるため、特定期間の統計を取るには 開始・終了時点のスナップショットを保存して差分を計算します。

SQL — スナップショット保存テーブルの作成と差分分析
-- スナップショット保存テーブル
CREATE TABLE pgss_snapshot AS
SELECT
    now()         AS snap_time,
    queryid,
    query,
    calls,
    total_exec_time,
    rows,
    shared_blks_hit,
    shared_blks_read
FROM pg_stat_statements
WHERE 1 = 0;  -- 構造だけ作成

-- スナップショット取得(計測開始時)
INSERT INTO pgss_snapshot
SELECT now(), queryid, query, calls, total_exec_time, rows, shared_blks_hit, shared_blks_read
FROM pg_stat_statements;

-- 計測終了時にもう一度 INSERT してから差分クエリ
WITH
  s1 AS (SELECT * FROM pgss_snapshot WHERE snap_time = (SELECT MIN(snap_time) FROM pgss_snapshot)),
  s2 AS (SELECT * FROM pgss_snapshot WHERE snap_time = (SELECT MAX(snap_time) FROM pgss_snapshot))
SELECT
    s2.query,
    s2.calls - s1.calls                       AS calls_diff,
    ROUND((s2.total_exec_time - s1.total_exec_time)::numeric, 2) AS exec_ms_diff,
    ROUND(((s2.total_exec_time - s1.total_exec_time) /
        NULLIF(s2.calls - s1.calls, 0))::numeric, 2)             AS avg_exec_ms,
    s2.rows - s1.rows                         AS rows_diff,
    s2.shared_blks_read - s1.shared_blks_read AS phys_reads_diff
FROM s1 JOIN s2 USING (queryid)
WHERE s2.calls > s1.calls
ORDER BY exec_ms_diff DESC
LIMIT 20;

7. 統計のリセット

SQL — リセット
-- 全統計をリセット(計測開始点のリセット)
SELECT pg_stat_statements_reset();

-- 特定のクエリのみリセット(PostgreSQL 12+)
SELECT pg_stat_statements_reset(userid, dbid, queryid)
FROM pg_stat_statements
WHERE query LIKE '%target_table%';