Skip to content

元ドキュメント: スロークエリの最適化

スロークエリの最適化

概要

スロークエリ(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 の分散環境では、シャードキーの活用とクロスノードアクセスの最小化が特に重要です。

詳細な仕様については、元ドキュメントをご参照ください。

Tencent Cloud プロダクトドキュメント