0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

100万件以上のデータを持つテーブルでのクエリ最適化テクニック

Posted at

1. インデックスの活用

まず最初に確認すべきなのが インデックスの設計です。
WHEREORDER BYJOIN に使われるカラムには適切なインデックスを設定しましょう。

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の方が便利で安定

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?