#01 データベースの高速化

スロークエリを発見する

データベースが遅い。そう感じたとき、あなたはどこから手を付けますか? 闇雲にインデックスを追加したり、クエリを書き直したりするのは得策ではありません。まず「何が遅いのか」を正確に把握することが、すべてのパフォーマンス改善の出発点です。

スロークエリログの仕組み クライアント (アプリ) SQL MySQL Server クエリを実行 実行時間を計測 結果 結果を返却 (高速 / 低速) 閾値チェック long_query_time 超過した場合 スロークエリログ /var/log/mysql/slow.log Query_time / Rows_examined を記録 設定パラメータ slow_query_log = 1 long_query_time = 1 slow_query_log_file = /var/log/... log_queries_not_using_indexes = 1
図1: スロークエリログの仕組み

スロークエリログとは

スロークエリ? 実行に時間がかかるSQLクエリのこと。MySQLのスロークエリログで記録・分析できる。ボトルネックの特定に不可欠で、`long_query_time` で閾値を設定する。 は、一定時間以上かかったクエリをファイルに記録する MySQL の機能です。どのクエリがボトルネックになっているかを把握するための、最も基本的なツールです。

スロークエリログの有効化

MySQL の設定ファイル(my.cnf または my.ini)に以下を追記します。

# /etc/mysql/mysql.conf.d/mysqld.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
  • slow_query_log = 1:スロークエリログを有効化
  • long_query_time:何秒以上のクエリを記録するか(1秒が一般的な出発点)
  • log_queries_not_using_indexes:インデックスを使わないクエリもすべて記録

実行中の MySQL に対して動的に変更することもできます。

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 1;

ログの読み方

スロークエリログの1エントリはこのような形式です。

# Time: 2026-04-10T09:23:14.123456Z
# User@Host: webapp[webapp] @ localhost []  Id: 42
# Query_time: 3.521045  Lock_time: 0.000123 Rows_sent: 1  Rows_examined: 845231
SET timestamp=1744277994;
SELECT * FROM orders WHERE status = 'pending';

Query_time が実際の実行時間、Rows_examined がスキャンした行数です。Rows_sent が1なのに Rows_examined が84万行というのは、典型的なインデックス不足のサインです。

mysqldumpslow でログを集計する

ログが大量になると生のテキストを読むのは困難です。MySQL に同梱されている mysqldumpslow を使うと、同一パターンのクエリを集計できます。

# 実行時間の長い順に上位10件を表示
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# 出現回数の多い順に表示
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log

出力例:

Count: 1842  Time=3.52s (6483s)  Lock=0.00s (0s)  Rows=1.0 (1842)
SELECT * FROM orders WHERE status = 'N'

Count: 1842 は1842回実行されたことを意味します。1回3.5秒のクエリが毎日数千回実行されているとすれば、その改善効果は絶大です。

pt-query-digest で詳細分析

mysqldumpslow より強力なのが Percona Toolkit の pt-query-digest です。

# インストール(Ubuntu/Debian)
sudo apt-get install percona-toolkit

# 基本的な分析
pt-query-digest /var/log/mysql/slow.log

# 直近1時間分だけ分析
pt-query-digest --since=3600 /var/log/mysql/slow.log

出力はクエリごとに合計時間・平均時間・最大時間・実行回数・Rows_examinedの統計が表示されます。「合計時間が最も長いクエリ」から手を付けるのが費用対効果の高いアプローチです。

パフォーマンススキーマを使う

MySQL 5.6以降で利用できるパフォーマンススキーマは、ファイルへの書き込みを伴わずにリアルタイムでクエリの統計情報を収集します。

-- 最もコストの高いクエリを確認
SELECT
  DIGEST_TEXT,
  COUNT_STAR AS exec_count,
  ROUND(SUM_TIMER_WAIT / 1e12, 3) AS total_sec,
  ROUND(AVG_TIMER_WAIT / 1e12, 6) AS avg_sec,
  SUM_ROWS_EXAMINED AS rows_examined
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

スロークエリログが「閾値以上のクエリだけ記録する」のに対し、パフォーマンススキーマはすべてのクエリの統計を蓄積します。long_query_time 未満でも頻繁に実行されるクエリが全体のボトルネックになっているケースを発見できるのが強みです。

ボトルネック発見の哲学

チューニングのワークフロー ① 計測 スロークエリログ パフォーマンスSCHEMA ② 分析 EXPLAIN で実行計画 優先度を決める ③ 改善 インデックス追加 クエリ書き換え ④ 再計測 Before/After を比較 改善しきれていない場合はサイクルを継続 優先順位のポイント 「合計影響時間 = 実行時間 × 実行回数」が最大のクエリから着手する 1回だけ遅いクエリより、100ms × 10,000回/日 のクエリのほうが改善効果は大きい
図2: チューニングのワークフロー

パフォーマンスチューニングには「計測 → 分析 → 改善 → 再計測」というサイクルが欠かせません。

  1. 計測:スロークエリログやパフォーマンススキーマで現状を数値化する
  2. 優先順位付け:合計影響時間(実行時間 × 実行回数)が最大のクエリから着手する
  3. 仮説を立てる:なぜ遅いのか?インデックス不足?データ量?クエリの構造?
  4. 改善する:インデックスを追加する、クエリを書き直す
  5. 再計測:改善前後を同じ指標で比較する

「直感で速そうなクエリを書く」より「遅いクエリを計測で特定して改善する」ほうが、はるかに確実で再現性のある成果が得られます。

次のエピソードでは、スロークエリを特定したあとの次のステップ—— EXPLAIN? MySQLがSQLをどのように実行するかの「実行計画」を表示するコマンド。インデックスが使われているか、何行スキャンしているかなどを確認でき、チューニングの出発点になる。 を使って実行計画を読む方法を学びます。