ちょっとリアルな話から始めよう。君が大手ネットショップの開発者として働いてると想像してみて。全部バッチリ動いてるんだけど、ひとつだけ問題があるんだ。月次売上レポートがめっちゃ遅くて、ユーザーはレポートが終わる前にもう一回注文できちゃうくらい。君もイライラ、マネージャーもイライラ、PostgreSQLサーバーはケトルみたいに沸騰してて、まるでアポカリプスの予感。もし一発で原因を見つけて直せたら、最高じゃない?
クエリパフォーマンス分析は、PostgreSQLの健康診断みたいなもの。どこが「ボトルネック」(痛みポイント)なのか見つけて、直すことでユーザー体験もシステムリソースも節約できるんだ。
PostgreSQLの中でクエリはどう動いてる?
例えば、こんなシンプルなクエリを書くとする:
SELECT * FROM products WHERE price > 100;
PostgreSQLはいきなりデータを取りに行くわけじゃない。まずクエリを解析して、どうやって実行するのがベストか考えてから、実際に動き出すんだ。
クエリ実行の主なステップ:
- パース。PostgreSQLはクエリの構文エラーをチェックして、中間表現に変換する。
- 最適化。クエリオプティマイザがいくつかの実行パターンを評価して、一番「コストが安い」(時間やリソース的に)ものを選ぶ。
- 実行。サーバーは選ばれたプラン通りにデータを取りに行く。
「ボトルネック」って何?
「ボトルネック」っていうのは、クエリの中で全体を遅くしてる部分のこと。たとえば、PostgreSQLがインデックスを使わずにテーブル全体をスキャン(Seq Scan)しちゃうと、クエリが遅くなる。あるいは、想定よりデータが多くて、サーバーがソートや結合、フィルタリングにめっちゃ時間かかる場合もある。
こういう部分がボトルネック。まずはここを見つけて最適化するのが大事!
クエリパフォーマンス分析のツール
PostgreSQLには、クエリの問題を見つけるための強力なツールがいくつかあるよ:
- EXPLAIN と EXPLAIN ANALYZE。 これらのコマンドで、PostgreSQLがクエリをどう実行するつもりか、または実際に実行してどんなパフォーマンスだったかを確認できる。
EXPLAIN: クエリを実行せずに実行プランだけ表示する。EXPLAIN ANALYZE: クエリを実際に実行して、リアルな実行プランとタイムメトリクスを表示する。
EXPLAINの使い方例:
EXPLAIN SELECT * FROM products WHERE price > 100;
出力例:
Seq Scan on products (cost=0.00..35.50 rows=5 width=72)
Filter: (price > 100)
ここでは「Seq Scan」つまりテーブル全体スキャンになってて、大きなテーブルだと非効率だよ。
- pg_stat_statements。 これは追加のエクステンションで、実行されたクエリを記録してくれる。こんなことが分かる:
- どんなクエリがサーバーで実行されてるか。
- 各クエリにどれくらい時間がかかってるか。
- クエリが返す行数やリソース消費量。
pg_stat_statementsを有効にするには:
- エクステンションを有効化:
CREATE EXTENSION pg_stat_statements;
- PostgreSQLの設定を調整:
postgresql.confファイルに以下を追加:
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
- PostgreSQLを再起動。
これでクエリを分析できるようになる:
SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
これで「一番重い」クエリ上位5件が、合計実行時間の多い順に表示されるよ。
- スロークエリログ。 PostgreSQLに、実行に時間がかかりすぎたクエリ(例えば1秒以上)をログファイルに記録させることもできる。
そのためにはpostgresql.confで:
log_min_duration_statement = 1000 # ミリ秒単位(1秒)
これで遅いクエリがPostgreSQLのログに記録される。
パフォーマンス分析の主なメトリクス
クエリパフォーマンスを分析するときは、次の主要なメトリクスに注目しよう:
- 実行時間。クエリの実行にかかった時間。速いほど良い!
- 行数。クエリが返す、またはスキャンする行数が想定より多いと問題かも。
- インデックス利用。インデックスを使うべきクエリが、シーケンシャルスキャン(
Seq Scan)してたら要最適化。 - バッファとディスク操作。ディスクアクセスが多いクエリは、メモリからデータを取るクエリより遅い。
これらの知識、どうやって実践する?
例1: 遅いクエリ
100より高い値段の商品を全部取得するクエリを書く:
SELECT * FROM products WHERE price > 100;
クエリが遅いと気づいたら、
EXPLAINを使ってみると:
Seq Scan on products (cost=0.00..35.50 rows=5 width=72)
Filter: (price > 100)
問題:priceカラムにインデックスがないから、テーブル全体スキャンになってる。
解決策:
インデックスを作成しよう:
CREATE INDEX idx_price ON products(price);
これでクエリはIndex Scanを使うようになる:
Index Scan using idx_price on products (cost=0.15..8.25 rows=5 width=72)
Index Cond: (price > 100)
例2: pg_stat_statementsで遅いクエリを特定
このコマンドで:
SELECT query, total_time, calls
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 1;
実行時間がめっちゃ長いクエリを発見。EXPLAIN ANALYZEで詳細を見て、修正したら速くなった!
EXPLAINやpg_stat_statementsなどのツールを使い始めると、クエリがどんどん速くなるし、PostgreSQLサーバーもスイス時計みたいに動くよ。次のレクチャーでは、EXPLAINのcost、rows、widthみたいなパラメータの詳細に突っ込んで、実行プランを本みたいに読めるようになろう!
GO TO FULL VERSION