ここが本番だよ:SQLクエリってただのコードの一行じゃなくて、データベースとの本気の会話なんだ。もし優しく"SELECT *"って囁けば、データベースは素直に理解してコマンドを実行してくれる。でも、構造化されてないSQL小説を投げつけたら、データベースは「え?」ってなって…そして遅くなる。
クエリ最適化は、データベースと分かりやすく簡潔な言葉で話すスキル。クエリが明確で効率的なら、実行も速いし、システムに負荷もかけないし、他のプロセスの邪魔にもならない。でも、イケてないクエリだとシステム全体が遅くなる:CPUやメモリを無駄に食うし、ディスクも余計な読み書きで忙しくなるし、データベースを使うアプリもモッサリしちゃう。
EXPLAIN ANALYZEは、そんな問題のある部分を見つけて、「どこでクエリが重くなってるか」を教えてくれる。まるで診断ツールみたいなもので、これがないとパフォーマンス改善は難しい。
クエリでよくある問題とその見つけ方
じゃあ、パフォーマンスを悪くする「容疑者」たちを紹介しよう。そのためにEXPLAIN ANALYZEコマンドを使うよ。
問題1: シーケンシャルスキャン (Seq Scan)
Seq Scan(シーケンシャルスキャン)は、PostgreSQLがテーブルの全行を1つずつ見てデータを探すこと。テーブルが小さいならいいけど、大きいとこれは地獄。
Seq Scanが使われてるかどうかを知るには? EXPLAIN ANALYZEで分析してみよう。例:
EXPLAIN ANALYZE
SELECT *
FROM students
WHERE student_id = 123;
結果はこんな感じ(Seq Scanに注目):
Seq Scan on students (cost=0.00..35.50 rows=1 width=72) (actual time=0.010..0.015 rows=1 loops=1)
どうやって解決する?
student_idにインデックスがなければ作ろう:
CREATE INDEX idx_student_id ON students(student_id);
その後、もう一度EXPLAIN ANALYZEを実行。今度はSeq ScanじゃなくてIndex Scanが出るはず。
問題2: 条件の選択性が低い
選択性っていうのは、目的のデータを見つけるために何行処理しなきゃいけないかってこと。もしフィルターがほぼ全テーブルをカバーしてたら、インデックスは役に立たない。
選択性が低いクエリの例:
EXPLAIN ANALYZE
SELECT *
FROM students
WHERE program = 'Computer Science';
もしテーブルの90%の学生がComputer Science専攻なら、インデックスがあってもSeq Scanになることがある。
クエリをどう改善する?
- クエリのロジックを見直そう:もしかしたら、もっと条件を追加してフィルターを絞れるかも。
- テーブルの統計情報が最新か確認しよう(これでPostgreSQLが選択性を正しく判断できる):
ANALYZE students;
- もしクエリが不必要にインデックスじゃなくてシーケンシャルスキャンを使ってたら、PostgreSQLにインデックスを使わせるようにしてみよう:
SET enable_seqscan = OFF;
問題3: 無駄なソート処理
ソート(Sort)は結構コストが高い操作。特にデータがメモリに収まらないときはヤバい。典型的なのはORDER BYを使う場合。
問題の例:
EXPLAIN ANALYZE
SELECT *
FROM students
ORDER BY last_name;
こんな感じの結果が出るかも:
Sort (cost=123.00..126.00 rows=300 width=45) (actual time=1.123..1.234 rows=300 loops=1)
どうやってソートを速くする? 特定のカラムでよくソートするなら、インデックスを作ろう:
CREATE INDEX idx_last_name ON students(last_name);
これでPostgreSQLはインデックスを使って、データをソート済みで取り出せるから、追加のソート処理を避けられる。
問題4: 制限(LIMIT)がない
SELECTで返す行数を制限しないと、必要なのが1行だけでもテーブル全体を処理しちゃうことがある。
こんな感じ:
EXPLAIN ANALYZE
SELECT *
FROM students
WHERE gpa > 3.5;
もしデータベースに100万行あって、gpa > 3.5で80%がヒットしたら、かなり待たされるよ。
トップ10人だけ欲しいなら、LIMITを使おう:
SELECT *
FROM students
WHERE gpa > 3.5
ORDER BY gpa DESC
LIMIT 10;
さらに、LIMITとOFFSETを組み合わせればページングもできる。
実行パラメータの管理: SET
PostgreSQLのSETコマンドは、セッションやクエリの動作パラメータを変更するために使う。これは一時的な設定で、今の接続だけに影響するんだ。
簡単に言うと、SETはPostgreSQLの「気分」をその場で変える方法で、グローバル設定をいじらなくてもいい。
どんなときに使う?
- レポート実行前に言語や日付フォーマットを変えたいとき。
- 重いクエリだけメモリを増やしたいとき。
- 大量データ投入時にログを一時的にオフにしたいとき。
- 一時的にスキーマの検索パス(
search_path)を変えたいとき。 - セキュリティ管理(例えば一時的にユーザー権限を下げる)したいとき。
基本構文
SET パラメータ = 値;
今のパラメータ値を確認するには:
SHOW パラメータ;
デフォルト値に戻すには:
RESET パラメータ;
複合的な最適化の例
例えば、こんな課題があるとしよう:Computer Science専攻でGPAが高い順に最新の10人の学生を探したい。元のクエリはこう:
SELECT *
FROM students
WHERE program = 'Computer Science'
ORDER BY gpa DESC
LIMIT 10;
クエリの分析: まず
EXPLAIN ANALYZEを実行:EXPLAIN ANALYZE SELECT * FROM students WHERE program = 'Computer Science' ORDER BY gpa DESC LIMIT 10;もしシーケンシャルスキャンやソートが出てきたら、最適化のサイン。
フィルター&ソート用の複合インデックス:
両方のカラムを含む複合インデックスを作ろう:
CREATE INDEX idx_program_gpa ON students(program, gpa DESC);改善の確認:
もう一度
EXPLAIN ANALYZEを実行。今度はこのインデックスが使われて、ソートやシーケンシャルスキャンが避けられてるはず。
クエリ最適化のやり方
まずは今の実行プランを分析しよう。
EXPLAIN ANALYZEで問題のある操作を見つける。ボトルネックを特定しよう。 一番時間がかかってるノードやリソースを食ってる部分を探す。
インデックスを作ろう。 どのカラムがフィルタやソートに使われてるか確認して、必要なインデックスを作成。
データ量を最小限に。
LIMITやOFFSET、そして正確なフィルタ条件を使おう。統計情報を最新に。
ANALYZEを実行して、PostgreSQLがデータ分布をちゃんと把握できるように。変更後はテスト! 最適化したらもう一度
EXPLAIN ANALYZEでパフォーマンスが良くなったか確認しよう。
次はどうする?
これでクエリ最適化の超速コースは終了!おめでとう!EXPLAIN ANALYZEで色々試せば試すほど、PostgreSQLの中身がどんどん分かってくるよ。そして覚えておいて:どんな魔法のインデックスも、クエリが複雑すぎたり曖昧だったら助けてくれない。SQLも他の言語と同じで、分かりやすさが命!
GO TO FULL VERSION