インデックスって、DBを速くするための超便利な仕組みだけど、「過ぎたるは及ばざるが如し」ってやつ。全部のインデックスが役立つわけじゃなくて、むしろ多すぎると逆効果になることもあるんだ。ちょっと意外だけど、マジでそう。詳しく見ていこう!
でっかい図書館を想像してみて。そこには本を探すためのカタログがいくつもある。例えば、著者別、ジャンル別、出版年別みたいな感じ。それぞれのカタログは本を探すのに便利だけど、もしカタログが多すぎて、タイトルの単語ごとや細かい情報ごとにカタログがあったらどう?逆に混乱して、探すのに時間がかかるし、カタログの管理も大変になるよね。
DBのインデックスも同じ。必要なデータを速く見つけるのに役立つけど、多すぎると、データを追加・更新するたびに全部のインデックスを更新しなきゃいけなくて超面倒。ディスクの容量も食うし、インデックスが多すぎるとDBがどれを使えばいいか迷っちゃうこともある。
だから、図書館のカタログと同じで、インデックスも「ちょうどいい数」が大事。必要で効率的なものだけに絞ろう!
じゃあ「PostgreSQL探偵ごっこ」しよう。例えば、同じカラムに3つもインデックスを作ったとする。パフォーマンス上がると思ってやったんだけど、実は…
- もしテーブルが学生のリストで、インデックスが3つあると、新しい学生を追加するたびに3つのインデックスを更新しなきゃいけない。全然「速く」なってないよね?
- しかも、こんなテーブルが10個もあって、それぞれインデックスだらけだったら?DB全体のパフォーマンスが地の底に落ちるよ。
インデックス作りすぎかどうか、どうやって見抜く?
まず最初にやるべきは、今どんなインデックスがあるかチェックすること。PostgreSQLなら、こんなコマンドで見れるよ:
\d テーブル名
このコマンドでテーブル、カラム、関連インデックスが一覧で出てくる。もし1つのテーブルにインデックスがめっちゃ多かったら、それは要注意。
もう一つ便利なのが、システムビューの pg_stat_user_indexes。どのインデックスがどれだけ使われてるか分かるから、「お荷物インデックス」を見つけられる:
SELECT
relname AS table_name,
indexrelname AS index_name,
idx_scan AS index_scans
FROM
pg_stat_user_indexes
WHERE
idx_scan = 0;
idx_scan が0なら、そのインデックスは一度も使われてないってこと。こういうのは削除候補だね。
インデックス作りすぎの例
ユーザーのテーブルを例にしよう:
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE,
username VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW()
);
で、インデックスが3つあるとする:
-- email用インデックス
CREATE INDEX idx_users_email ON users (email);
-- username用インデックス
CREATE INDEX idx_users_username ON users (username);
-- created_at用インデックス
CREATE INDEX idx_users_created_at ON users (created_at);
で、よくやるクエリはこんな感じ:
- emailでユーザー検索
- usernameでユーザー検索
created_atでユーザーをソート
一見インデックスが役立ちそうだけど、もしこれらのクエリが週1回しか走らないとか、ほとんど使われないなら、インデックスは無駄。しかも、全然使われてないインデックスは、INSERTやUPDATEのたびに余計な負荷をかけるだけ。
例えば、usersテーブルにこんなデータがあるとする:
| user_id | username | created_at | |
|---|---|---|---|
| 1 | alex.lin@mail.com | alexlin | 2024-06-15 10:23:00 |
| 2 | anna.min@mail.com | annamin | 2024-06-16 12:47:00 |
| 3 | otto.song@mail.com | ottosong | 2024-06-17 08:30:00 |
| 4 | maria.chi@mail.com | mariachi | 2024-06-18 14:10:00 |
もしusernameでの検索がほとんど無いなら、idx_users_usernameインデックスは一度も使われてない(idx_scan = 0)し、削除してOK。
つまり、インデックスは便利だけど、ちゃんと考えて使おう。必要でよく使われるインデックスだけに絞るのがベスト!
インデックス作りすぎを防ぐには?
- インデックスの利用状況を分析する。 さっきも言ったけど、
pg_stat_user_indexesでインデックスの利用状況をチェックしよう。ほとんど使われてないなら、削除しても大丈夫:
DROP INDEX IF EXISTS インデックス名;
- よく使うクエリだけにインデックスを作る。 インデックスを追加する前に、自分にこう聞いてみて:
- このカラム、
WHEREやORDER BY、GROUP BYでよく使う? - テーブルのデータ量は多い?
- インデックス無しだと本当に遅い?
どれか一つでも「いいえ」なら、インデックスは不要かも。
- 複合インデックスを使う。 複数カラムをよく一緒に使うなら、個別にインデックスを作るより複合インデックスが効率的:
CREATE INDEX idx_users_email_username ON users (email, username);
これで、email かつ usernameで絞るクエリが速くなるよ。
- 定期的にインデックスを見直す。 DBが大きくなると、クエリの内容も変わる。1年前は便利だったインデックスも、今は不要かも。定期的にインデックスをチェックして、使ってないものは削除しよう。
インデックス最小化の例
さっきのusersテーブルに戻ろう。3つのインデックスをこう最適化できる:
created_atでのソートがほとんど無いなら、そのインデックスは削除。emailとusernameの個別インデックスをやめて、複合インデックスにする:
CREATE INDEX idx_users_email_username ON users (email, username);
まとめ:バランスのコツは?
プログラミングの多くと同じで、ここでも「少ない方がいい」っていうミニマリズムが大事。全部のカラムにインデックスを付ける必要はないし、なんでそのインデックスが必要なのか、どれだけクエリが速くなるのかを考えよう。インデックスをやたら追加する人より、その効果を理解してうまく使う人が「できるエンジニア」だよ。
この知識を使えば、インデックス作りすぎの悲劇を防いで、君のDBをチーター並みに速く、無駄なインデックスで重くなったカメみたいにしないで済むよ!
GO TO FULL VERSION