チューニング
スロークエリの特定と対処 — slow log / pg_stat_statements
遅いクエリを特定する方法。MySQL スロークエリログ・PostgreSQL pg_stat_statements・auto_explain。
まず「どのクエリが重いか」を知る
チューニングの鉄則は「遅いものから直す」です。「なんとなく遅い気がする」クエリをいじるより、実測で上位のクエリを順に潰すほうが投資対効果が圧倒的に高い。
探すべきは 2 種類:
- 1 回あたりが遅いクエリ(レイテンシ観点)
- 合計時間が大きいクエリ(スループット / DB 負荷観点)。1 回は速くても 1 秒 1000 回飛んでいるなら、それが DB を食っている
MySQL: スロークエリログ
MySQL にはスロークエリログがあり、一定時間以上かかったクエリを自動でファイルに記録できます。設定:
slow_query_log = ONlong_query_time = 1(秒。0.1 など少数も可)slow_query_log_file = /var/log/mysql/slow.loglog_queries_not_using_indexes = ON(インデックス未使用も記録、ただし量が増えるので開発環境向け)
ログは mysqldumpslow や pt-query-digest(Percona Toolkit)で集計するのが定番。同じパターンのクエリをまとめて、合計時間・呼び出し回数ランキングを出せます。
sql
-- 実行中のセッションで有効化
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5;
-- 集計 (シェル)
-- $ pt-query-digest /var/log/mysql/slow.log | headPostgreSQL: pg_stat_statements
PostgreSQL にはpg_stat_statementsという拡張があり、クエリをパラメータ正規化した単位で集計してくれます。呼び出し回数、合計・平均時間、取得行数、バッファヒット率まで一発で見える超便利ツール。本番で必ず有効化しましょう。
代表的な集計クエリ:
- total_exec_time 降順 → DB 全体で最も重いクエリ
- mean_exec_time 降順 → 1 回あたり遅いクエリ
- calls 降順 → 高頻度クエリ(N+1 発見にも使える)
sql
-- 有効化 (postgresql.conf: shared_preload_libraries='pg_stat_statements')
CREATE EXTENSION pg_stat_statements;
-- 合計時間で上位 10 クエリ
SELECT
round(total_exec_time::numeric, 1) AS total_ms,
calls,
round(mean_exec_time::numeric, 2) AS mean_ms,
substring(query, 1, 80) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;ボトルネック発見から修正までの流れ
実務での典型フローはこうなります:
- 上位リストを出す: pg_stat_statements / slow log で重いクエリ Top 10 を特定
- EXPLAIN ANALYZE で計画を見る(推定と実測の乖離、Seq Scan / filesort / temporary 等に注目)
- 仮説: インデックス不足 / 列順ミス / N+1 / 型キャスト / OR 条件分解不足 / 統計情報古い…
- 試しに修正: 開発環境で
CREATE INDEX CONCURRENTLYやクエリ書き換えを適用し、再度 EXPLAIN ANALYZE で検証 - 本番適用: 負荷時間帯を避け、
CONCURRENTLY/ オンライン DDL で安全に投入 - 再計測: 翌日、pg_stat_statements の該当クエリが下がっているか確認
アプリ側の指標も見る
DB 単体のメトリクスだけ見ていると、アプリ側で組み立てた後に遅くなっているケースを見逃します。例えば:
- 1 クエリは速いが、N+1 で 200 回発行されている
- アプリが大量に取得した行をメモリ上でフィルタしている(DB に WHERE を任せれば一発)
- ネットワーク往復 RTT がボトルネック
APM(Datadog / New Relic / Sentry Performance / OpenTelemetry)で「1 リクエスト中の DB 時間」をまず見るのが確実です。DB 側だけ最適化しても、ネットワークやアプリが原因ならレスポンスは改善しません。
