元ドキュメント: スロークエリの最適化
スロークエリの最適化
概要
スロークエリ(Slow Query)は、設定された閾値を超える実行時間を要するクエリです。スロークエリの特定と最適化は、データベースのパフォーマンス改善において最も効果の高いアプローチの一つです。
TDSQL Boundless では、分散環境特有の要因によってクエリが遅延する場合があるため、従来の最適化手法に加えて、分散アーキテクチャを考慮した最適化が必要です。
スロークエリの特定
スロークエリログの有効化
sql
-- スロークエリログの設定確認
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
-- スロークエリの閾値設定(秒)
SET GLOBAL long_query_time = 1;スロークエリの分析
sql
-- スロークエリログのサマリ確認
-- mysqldumpslow コマンドの使用
-- mysqldumpslow -s t -t 10 /path/to/slow-query.logパフォーマンススキーマを使用した分析
sql
-- 実行時間の長いクエリの特定
SELECT
DIGEST_TEXT,
COUNT_STAR AS exec_count,
ROUND(AVG_TIMER_WAIT / 1000000000, 2) AS avg_time_ms,
ROUND(SUM_TIMER_WAIT / 1000000000, 2) AS total_time_ms,
SUM_ROWS_EXAMINED AS rows_examined,
SUM_ROWS_SENT AS rows_sent
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 20;一般的な最適化手法
1. インデックスの追加・最適化
最も一般的かつ効果的な最適化手法です。
sql
-- 改善前:フルテーブルスキャン
SELECT * FROM orders WHERE customer_email = 'user@example.com';
-- インデックスの追加
CREATE INDEX idx_customer_email ON orders(customer_email);
-- 改善後:インデックスを使用したアクセス
SELECT * FROM orders WHERE customer_email = 'user@example.com';2. SELECT * の回避
必要なカラムのみを明示的に指定することで、データ転送量を削減できます。
sql
-- 非推奨
SELECT * FROM orders WHERE user_id = 1001;
-- 推奨
SELECT order_id, order_date, total_amount FROM orders WHERE user_id = 1001;3. LIMIT の活用
大量のデータを返すクエリでは、LIMIT を使用して結果セットを制限します。
sql
-- 非推奨:全件取得
SELECT * FROM logs WHERE created_at > '2026-01-01' ORDER BY created_at;
-- 推奨:必要な件数のみ取得
SELECT * FROM logs WHERE created_at > '2026-01-01' ORDER BY created_at LIMIT 100;JOIN の最適化
結合順序の最適化
オプティマイザは通常、最適な結合順序を選択しますが、必要に応じてヒントを使用できます。
sql
-- 小さいテーブルを駆動テーブルにする
SELECT /*+ JOIN_ORDER(small_table, large_table) */
l.*, s.name
FROM large_table l
JOIN small_table s ON l.ref_id = s.id
WHERE s.status = 'active';結合条件へのインデックス付与
sql
-- 結合カラムにインデックスがない場合、ネステッドループ結合が非効率になる
-- 結合条件のカラムにインデックスを作成する
CREATE INDEX idx_ref_id ON large_table(ref_id);分散環境での結合の注意点
TDSQL Boundless では、結合対象のデータが異なるノードに分散している場合、ノード間のデータ転送が発生します。
sql
-- 効率的:シャードキーでの結合(同一ノードで処理可能)
SELECT o.*, u.name
FROM orders o
JOIN users u ON o.user_id = u.user_id;
-- 非効率:シャードキー以外での結合(クロスノードデータ転送が発生)
SELECT o.*, p.name
FROM orders o
JOIN products p ON o.product_id = p.product_id;サブクエリの書き換え
相関サブクエリの JOIN への変換
sql
-- 非効率:相関サブクエリ
SELECT * FROM orders o
WHERE o.total_amount > (
SELECT AVG(total_amount)
FROM orders
WHERE user_id = o.user_id
);
-- 効率的:JOIN を使用した書き換え
SELECT o.*
FROM orders o
JOIN (
SELECT user_id, AVG(total_amount) AS avg_amount
FROM orders
GROUP BY user_id
) avg_o ON o.user_id = avg_o.user_id
WHERE o.total_amount > avg_o.avg_amount;IN サブクエリの EXISTS への変換
sql
-- 場合によって非効率
SELECT * FROM users
WHERE user_id IN (SELECT user_id FROM orders WHERE status = 'pending');
-- EXISTS を使用した書き換え
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id AND o.status = 'pending'
);分散環境でのクエリ最適化ポイント
1. シャードキーの活用
sql
-- シャードキーを条件に含めてシングルノードクエリにする
SELECT * FROM orders WHERE user_id = 1001 AND order_date > '2026-01-01';2. クロスノード集約の最小化
sql
-- 非推奨:全ノードをスキャンする集約
SELECT COUNT(*) FROM orders;
-- 推奨:条件を絞り込んだ集約
SELECT COUNT(*) FROM orders WHERE user_id = 1001;3. パーティションプルーニングの活用
sql
-- パーティションキーを条件に含める
SELECT * FROM time_series_data
WHERE partition_key = '2026-03'
AND event_time > '2026-03-01 00:00:00';まとめ
スロークエリの最適化は、EXPLAIN による分析、適切なインデックスの設計、JOIN やサブクエリの書き換えを組み合わせて行います。TDSQL Boundless の分散環境では、シャードキーの活用とクロスノードアクセスの最小化が特に重要です。
詳細な仕様については、元ドキュメントをご参照ください。