未分類
DBのレスポンスが遅い時の調査方法
まず切り分け
「DBが遅い」と言われたら、最初にやるのは本当にDBが遅いのかという確認である。
-- 現在実行中のクエリを確認(MySQL)
SHOW PROCESSLIST;
-- PostgreSQLなら
SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;
長時間走っているクエリがあれば、そいつが犯人候補である。なければアプリ側のコネクションプールの枯渇とか、ネットワークレイテンシを疑う。
スロークエリログを見る
本番環境なら、大抵有効化されているはずである。
-- MySQL: スロークエリの設定確認
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
ログの場所は/var/log/mysql/slow.logあたりである。pt-query-digestで集計すると、どのクエリが何回呼ばれて合計何秒食っているか一発でわかる。
pt-query-digest /var/log/mysql/slow.log
EXPLAIN で実行計画を見る
問題のクエリを特定したらEXPLAINで実行計画を見る。
EXPLAIN SELECT * FROM orders
WHERE user_id = 123
AND created_at > '2024-01-01'
ORDER BY created_at DESC
LIMIT 100;
見るべきポイント:
| 項目 | ヤバいパターン |
|---|---|
| type | ALL(フルスキャン) |
| rows | 想定より桁違いに多い |
| Extra | Using filesort, Using temporary |
| key | NULL(インデックス使われてない) |
よくある原因と対処
1. インデックスが効いてない
-- インデックスの確認
SHOW INDEX FROM orders;
-- 複合インデックスの順序が重要
-- WHERE user_id = ? AND created_at > ? なら
CREATE INDEX idx_user_created ON orders(user_id, created_at);
-- user_id が先。選択性の高い方を先に。
2. N+1問題
アプリ側のログで同じクエリが大量に流れていたらこれである。ORMのeager loadingで解決する。
# Rails の例
# Bad: N+1
User.all.each { |u| u.posts }
# Good: eager load
User.includes(:posts).each { |u| u.posts }
3. ロック待ち
-- MySQL: ロック状況の確認
SHOW ENGINE INNODB STATUS\G
-- PostgreSQL
SELECT * FROM pg_locks WHERE NOT granted;
長時間トランザクションが開きっぱなしとか、デッドロックとかが原因である。アプリ側でトランザクションの粒度を見直す。
4. 統計情報が古い
古い統計情報を元に、オプティマイザが変な実行計画を選んでいることがある。
-- MySQL
ANALYZE TABLE orders;
-- PostgreSQL
ANALYZE orders;
5. バッファプール/shared_buffers が足りない
-- MySQL: バッファプールのヒット率
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
-- read_requests に対して reads が多いとディスクI/Oが多い
-- PostgreSQL
SELECT
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as hit_ratio
FROM pg_statio_user_tables;
ヒット率が95%を切っていたら、メモリを増やすことを検討する。
調査の流れまとめ
図を読み込み中...
1. SHOW PROCESSLIST で現在の状況確認
↓
2. スロークエリログで犯人特定
↓
3. EXPLAIN で実行計画確認
↓
4. インデックス追加 or クエリ書き換え or 設定チューニング
↓
5. 効果測定
面接で聞かれがちな追加質問
「インデックス張ったのに効かないケースは?」
- カーディナリティが低い(性別とか)
- 関数を使ってる(
WHERE YEAR(created_at) = 2024) - 型の暗黙変換(文字列カラムに数値で検索)
- LIKE の前方一致以外(
LIKE '%keyword') - OR 条件で片方にインデックスがない
「本番で急に遅くなった原因として何を疑う?」
- データ量の増加で実行計画が変わった
- 統計情報の自動更新タイミング
- ロック競合の増加
- ディスクI/Oの飽和(他のバッチ処理と競合など)
- コネクションプールの枯渇
「読み取りと書き込みどっちが遅い?」
切り分けが重要である。書き込みが遅いならレプリケーション遅延やディスクI/Oを疑う。読み取りだけが遅いならインデックスかキャッシュの問題である。