はじめに
以前、DBインデックスの基本と効果について記事を書いた。
しかし実務では、次のような誤解をよく見る。
「遅いなら、とりあえずインデックス貼ればよくない?」
結論から言うと、
インデックスは「付けすぎると確実にパフォーマンスを落とす」。
この記事では、
「これは絶対にダメ」なインデックスの張り方を、
理由・実例・エビデンス付きでまとめる。
絶対にダメ①:使われていないインデックスを大量に貼る
❌ よくある例
CREATE INDEX idx_users_name ON users(name);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_created_at ON users(created_at);
CREATE INDEX idx_users_updated_at ON users(updated_at);
※ 実際に使われているのは email のみ。
なぜダメ?
-
INSERT / UPDATE / DELETE のたびに
- 全インデックスを更新する必要がある
- 読み取りは速くならないのに
- 書き込み性能だけ確実に悪化
- ストレージ消費増
- オプティマイザの判断コストも増える
📉 「読まれないインデックス」は、純粋な負債
エビデンス
-
MySQL公式
Indexes slow down INSERT, UPDATE, and DELETE operations
https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html -
PostgreSQL公式
Indexes add overhead to write operations
https://www.postgresql.org/docs/current/indexes-intro.html
絶対にダメ②:SELECT条件を考えずに単体インデックスを乱立
❌ よくある例
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created_at ON orders(created_at);
実際のクエリ:
SELECT *
FROM orders
WHERE user_id = 10
AND status = 'paid'
ORDER BY created_at DESC;
なぜダメ?
- MySQLは基本的に1つのインデックスしか使えない
- 結果:
- どれか1本しか効かない
- 残りは無意味
本来必要なのは:
CREATE INDEX idx_orders_user_status_created
ON orders(user_id, status, created_at);
エビデンス
- MySQL Optimizer
The optimizer uses only one index per table per query block
https://dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html
絶対にダメ③:カーディナリティが低いカラムに単体インデックス
❌ よくある例
CREATE INDEX idx_users_gender ON users(gender);
CREATE INDEX idx_users_is_deleted ON users(is_deleted);
gender: 男 / 女 / 未設定
is_deleted: true / false
なぜダメ?
- 値の種類が少ない(低カーディナリティ)
- インデックスで絞れない
- 全件スキャンの方が速い
📌 MySQLは賢いので
→ インデックスを無視することすらある
エビデンス
- MySQL公式
Indexes are less useful for columns with low cardinality
https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html
絶対にダメ④:WHEREで使わないカラム順の複合インデックス
❌ よくある例
CREATE INDEX idx_logs_created_user
ON logs(created_at, user_id);
クエリ:
SELECT *
FROM logs
WHERE user_id = 5;
なぜダメ?
- 複合インデックスは「左端一致」が原則
-
created_atを指定していないため- このインデックスは使われない
正解:
CREATE INDEX idx_logs_user_created
ON logs(user_id, created_at);
エビデンス
- MySQL Leftmost Prefix
https://dev.mysql.com/doc/refman/8.0/en/multiple-column-indexes.html
絶対にダメ⑤:「とりあえず」で全部 INDEX を貼る運用
❌ よくある運用
- 機能追加のたびに
- 「遅そうだから一旦インデックス」
- 削除されない
- 誰も使っているか把握していない
なぜダメ?
- DBは「育つ」
- クエリは「変わる」
- インデックスは放置すると腐る
✔ 正しい運用
-
EXPLAINを必ず見る - 定期的に
- 使われていないインデックスを棚卸し
- MySQLなら:
SHOW INDEX FROM table_name;
+ slow query log / performance_schema
エビデンス
- MySQL Performance Schema
https://dev.mysql.com/doc/refman/8.0/en/performance-schema.html
まとめ:インデックスは「設計」であって「保険」じゃない
| やってはいけない | 理由 |
|---|---|
| 使われないインデックス | 書き込み性能低下 |
| 単体インデックス乱立 | 効かない |
| 低カーディナリティ | 無意味 |
| カラム順ミス | 完全に無効 |
| 付けっぱなし | 技術的負債 |
おわりに
インデックスは
「速くする魔法」ではなく「正しく使えば効く刃物」。
- 貼る前に
EXPLAIN - 貼った後も定期的に見直す
これができると
DBが一気に「分かってるエンジニア感」出る。