1. インデックスの活用
まず最初に確認すべきなのが インデックスの設計です。
WHERE
や ORDER BY
、JOIN
に使われるカラムには適切なインデックスを設定しましょう。
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_created_at ON users(created_at);
✳︎インデックスが多すぎると INSERT/UPDATE
が遅くなるため注意。
2. クエリの書き方を見直す
-
SELECT *
を避け、必要なカラムのみ指定しましょう。
-- Bad
SELECT * FROM users WHERE status = 'active';
-- Good
SELECT name, email FROM users WHERE status = 'active';
-
WHERE
句で関数を使わないようにする
-- Bad
WHERE YEAR(created_at) = 2024
-- Good
WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'
- 大量データのページネーション最適化
大量のデータに対する OFFSET
の使用は、パフォーマンスに悪影響を与えます。
-- 遅くなる例
SELECT * FROM users ORDER BY id LIMIT 1000000, 10;
-- 高速な方法
SELECT * FROM users WHERE id > 1000000 ORDER BY id LIMIT 10;
このように「カーソルベースのページネーション」の方が効率的です。
- クエリをキャッシュする
変更が少ないデータであれば、クエリ結果をキャッシュするのも一つの手です。Laravelの場合、次のように実装できます。
$data = Cache::remember('active_users', 60, function () {
return DB::table('users')->where('status', 'active')->get();
});
- EXPLAINでボトルネックを発見
クエリのパフォーマンスを分析するには、EXPLAIN
を使って実行計画を確認しましょう。
EXPLAIN SELECT * FROM users WHERE email = 'abc@example.com';
- JOINを使用する際の最適化
・必要なテーブルだけをJOINする
・INNER JOIN と LEFT JOIN を正しく使い分ける
-- 遅くなる例
SELECT users.*, posts.title
FROM users
LEFT JOIN posts ON users.id = posts.user_id
WHERE posts.status = 'published';
-- より適切な例(INNER JOIN)
SELECT users.*, posts.title
FROM users
INNER JOIN posts ON users.id = posts.user_id
WHERE posts.status = 'published';
・JOIN 対象のカラムにインデックスを張る
CREATE INDEX idx_posts_user_id ON posts(user_id);
・サブクエリや WITH句 (CTE) の活用
WITH active_posts AS (
SELECT * FROM posts WHERE status = 'published'
)
SELECT users.name, active_posts.title
FROM users
JOIN active_posts ON users.id = active_posts.user_id;
- パーティショニング(Partitioning)の活用
CREATE TABLE users (
id INT NOT NULL,
name VARCHAR(100),
created_at DATE
)
PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
- マテリアライズドビュー
CREATE MATERIALIZED VIEW active_users_summary AS
SELECT user_id, COUNT(*) AS total_posts
FROM posts
WHERE status = 'active'
GROUP BY user_id;
3. NoSQLに変わって、利用する
NoSQLを検討すべきタイミングは?
状況 | NoSQLを使うべきか? |
---|---|
書き込みや読み込みが多く、リアルタイム性が求められるシステム | ✅ 非常に適している |
データ構造(スキーマ)が頻繁に変化する | ✅ マイグレーション不要で柔軟 |
複数のテーブルをJOINする複雑なクエリが必要 | ❌ 不向き(RDBMSが適している) |
単純なCRUDやレポート機能が中心のアプリケーション | ❌ SQLの方が便利で安定 |
以上。