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?

MySQLが遅い時に最初に確認する5つのこと

0
Posted at

はじめに

「最近DBが重い」「このクエリなんで遅いんだろう」——そう思ったとき、どこから手を付ければいいか迷うことがあります。

この記事では、MySQL のパフォーマンス問題を調査するときに最初に確認すべき5つのポイントを、実際に使えるSQLとコマンド付きで紹介します。

難しい理論より「まずこれを実行する」を重視して書きました。

確認する順番

① EXPLAIN でクエリを解析する
② インデックスが使われているか確認する
③ スロークエリログを有効にする
④ SELECT * を使っていないか見直す
⑤ N+1 クエリが発生していないか確認する

① EXPLAIN でクエリを解析する

遅いクエリの前に EXPLAIN を付けるだけで、MySQLがどのようにそのクエリを実行するかを確認できます。

EXPLAIN SELECT * FROM orders WHERE user_id = 123;

注目するカラム

カラム 確認ポイント
type ALL(フルスキャン)が出たら要注意
key 使用されているインデックス名。NULLならインデックス未使用
rows 走査する推定行数。多いほど重い
Extra Using filesortUsing temporary が出たら改善余地あり

悪い例(type: ALL)

+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | orders | ALL  | NULL          | NULL | NULL    | NULL | 100000 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

100,000行を全部スキャンしています。これは典型的な「インデックス不足」のサインです。


② インデックスが使われているか確認する

テーブルのインデックス一覧を確認

SHOW INDEX FROM orders;

WHERE句のカラムにインデックスがない場合は追加する

-- インデックスを追加
ALTER TABLE orders ADD INDEX idx_user_id (user_id);

-- 追加後にEXPLAINで確認
EXPLAIN SELECT * FROM orders WHERE user_id = 123;

追加後に type: refkey: idx_user_id と表示されれば成功です。

複合インデックスの順番に注意

-- WHERE user_id = ? AND status = ? のクエリに対して
-- ◎ 良い:よく絞り込むカラムを先頭に
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);

-- △ 悪い:順番が逆だと効果が薄い場合がある
ALTER TABLE orders ADD INDEX idx_status_user (status, user_id);

③ スロークエリログを有効にする

「どのクエリが遅いか分からない」という場合は、スロークエリログを有効にして記録します。

現在の設定を確認

SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

一時的に有効化する(再起動不要)

-- スロークエリログをON
SET GLOBAL slow_query_log = 'ON';

-- 1秒以上かかったクエリを記録
SET GLOBAL long_query_time = 1;

-- ログファイルのパスを確認
SHOW VARIABLES LIKE 'slow_query_log_file';

my.cnf に書いて永続化する場合

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1  # インデックス未使用クエリも記録

mysqldumpslow でログを集計する

# 実行時間が長い順にTop10を表示
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

④ SELECT * を使っていないか見直す

SELECT * は手軽ですが、不要なカラムまで取得するためデータ転送量が増えます。特にTEXT型やBLOB型のカラムがある場合は影響が大きいです。

-- ❌ 悪い例:全カラム取得
SELECT * FROM products WHERE category_id = 5;

-- ✅ 良い例:必要なカラムだけ取得
SELECT id, name, price FROM products WHERE category_id = 5;

また、SELECT * はインデックスオンリースキャン(カバリングインデックス)が効かなくなるため、パフォーマンス上も不利です。


⑤ N+1 クエリが発生していないか確認する

N+1問題は、アプリケーション側で1回のクエリの結果に対してN回のクエリを発行してしまう問題です。DBのログや SHOW PROCESSLIST では気づきにくいため注意が必要です。

典型的なN+1の例(PHP)

// ❌ N+1が発生するコード
$users = $db->query("SELECT id, name FROM users LIMIT 100");
foreach ($users as $user) {
    // ユーザーごとに1クエリ発行 → 合計101クエリ!
    $orders = $db->query("SELECT * FROM orders WHERE user_id = {$user['id']}");
}

JOINで1回にまとめる

-- ✅ 1回のクエリで取得
SELECT
    u.id,
    u.name,
    COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
LIMIT 100;

現在の接続・クエリ状況を確認

-- 実行中のクエリ一覧
SHOW FULL PROCESSLIST;

-- 長時間実行中のクエリ(10秒以上)
SELECT * FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 10
ORDER BY time DESC;

まとめ

MySQLが遅いと感じたら、まずこの順番で確認してみてください。

  1. EXPLAIN でフルスキャン(type: ALL)を検出
  2. SHOW INDEX でインデックスの有無を確認し、必要なら追加
  3. スロークエリログで遅いクエリを洗い出す
  4. SELECT * を必要なカラムだけに絞る
  5. N+1クエリをJOINやサブクエリでまとめる

多くのケースはインデックスを適切に追加するだけで劇的に改善します。まずは EXPLAIN を習慣にするところから始めると良いと思います。


DBの設計・最適化を相談したい方へ

「クエリが遅い」「テーブル設計を見直したい」「データ移行をお願いしたい」など、データベース周りの受託相談を受け付けています。

🌐 https://datarou.com

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?