忍者ブログ
IT関係の小作人労働の日々の日記です。 最近データベースが好きです。 インフラ構築、DB構築、アプリケーション開発・・・何でも屋です。 何でもできそうで、何にもできない。

【DBテクニック】SQLの実行速度を劇的に変える「チューニングの定石」


同じ結果を得るSQLでも、書き方ひとつでデータベース内部の「仕事量」は天と地ほど変わります。今回は、実行計画を意識した「現場で即効性のある」チューニングの一般事項を整理しました。

1. 検索アルゴリズムを最適化する

【 現場の感触 】 データベースに「無駄な探索」をさせないのが基本です。特に存在確認などは、最後まで数えるか、見つかった瞬間に止めるかで雲泥の差が出ます。

COUNTよりEXISTS:1件でも条件に合う行が見つかれば探索を終了するため、全件スキャンするCOUNTより圧倒的に高速です。
ORよりIN:ORを多用するとインデックスが効かなくなる場合がありますが、IN演算子(定数リスト)はオプティマイザが最適化しやすく、実行計画が安定します。
「<」「>」よりBETWEEN:範囲指定が明確になり、インデックスレンジスキャンの効率が上がります。

2. 余計な「ソート」と「スキャン」を削る

【 現場の感触 】 データベースにとって最も重い処理の一つが「重複排除(ソート)」と「全表スキャン」です。これらを回避する選択がパフォーマンスの鍵です。

UNIONよりUNION ALL:UNIONは重複を消すために内部で「ソート」を強制します。重複がないと分かっているなら、ソート不要のUNION ALLが鉄則です。
COUNT(*)よりCOUNT(主キー):製品によりますが、主キーを指定することで「インデックスだけを見れば済む(Index Only Scan)」状態になり、データ本体へのアクセスを減らせる場合があります。
インデックスの作成:言うまでもなく基本中の基本。WHERE句やJOINキーへの適切なインデックス配置が全ての土台です。

3. 解析器(オプティマイザ)のクセを掴む

【 現場の感触 】 SQLは「書いた順序」が評価に影響することがあります。データベースの解析エンジンがどう動くかを意識して記述しましょう。

IN演算子の評価順序:一般にINの中身は「左から順に」評価されます。ヒットする確率が高い値を左に置くことで、判定コストを下げられます。
WHERE句の記述順序:多くのDBではWHERE句に書かれた順にフィルターをかけます。データ件数をより大きく絞り込める条件を「先に」書くことで、後続の判定対象を減らすのがセオリーです。

4. まとめ:チューニングは「DBとの対話」

今回紹介した項目は、いずれも「DBの内部リソース(CPU・メモリ・I/O)をいかに節約するか」に直結しています。

改善のチェックリスト
1. 重複排除(UNION)を無意識に使っていないか?
2. 全件カウント(COUNT)で存在確認していないか?
3. WHERE句の条件順序は最適か?


一つ一つは小さな工夫ですが、大量データを扱う本番環境ではこの積み重ねが「100倍の速度差」となって現れます。実行計画(EXPLAIN)を確認しながら、最適な一文を追求していきましょう。


PR

【データベースの知識】SQLパフォーマンスチューニングの即効ヒント


同じ結果を得るSQLでも、書き方ひとつでデータベース内部の「仕事量」は劇的に変わります。今回は、明日からの開発でそのまま使える、実行速度を劇的に改善するための6つのTipsを紹介します。

1. 実行効率を高める書き方の基本

【 基本 】 データベースエンジンにとって、無駄なスキャンや重複排除(ソート)は最大の敵です。まずは、より効率的な演算子や関数を選ぶことから始めましょう。

COUNTよりEXISTS:1件見つかった瞬間に検索を終了するため、全件カウントするより圧倒的に高速です。
UNIONよりUNION ALL:UNIONは重複を消すために内部で「ソート」が発生します。重複がないと分かっているならALL一択です。
ORよりIN:ORを多用するとインデックスが効かなくなる場合があります。複数の値を指定するならIN演算子の方が最適化されやすいです。

2. インデックスとデータアクセスの最適化

【 ポイント 】 インデックスはただ作るだけでなく、「使われるように書く」のがコツです。また、読み込むデータの範囲をいかに絞り込むかが勝負を分けます。

インデックスの作成:WHERE句やJOINの結合キーには必ずインデックスを検討しましょう。
COUNT(*)より主キー参照:製品によっては、主キーを指定することでインデックススキャンのみで処理が完結し、高速化する場合があります。
範囲指定はBETWEEN:`col > 10 AND col < 20` と書くより、`BETWEEN` を使う方がオプティマイザが範囲を正しく認識しやすく、読みやすいSQLになります。

3. まとめ:チューニングの優先順位

闇雲に修正するのではなく、まずは以下の表を参考に、コストの高い部分から手を入れていきましょう。

改善項目修正前推奨(修正後)効果の理由
存在確認 COUNT(*) > 0 EXISTS 見つかったら即終了
集合演算 UNION UNION ALL ソート処理をスキップ
複数指定 OR ... OR ... IN ( ... ) 可読性と解析効率の向上


パフォーマンスチューニングに「銀の弾丸」はありませんが、これらの定石を組み合わせることで、システムのレスポンスは確実に見違えるはずです。

【データベースの知識】SQLが実行されるまでの内部ステップ


私たちがSQLを1行実行したとき、データベースの内部では瞬時にいくつかの工程が実行されています。この「実行ステップ」を知ることで、なぜ同じ結果を返すクエリでも速度が変わるのか、その理由が見えてきます。

1. SQL実行の4つの主要ステップ

【 基本 】 送信されたSQL文は、データベースエンジンによって以下の順序で処理されます。特に「実行計画」の作成は、クエリの成否を分ける心臓部です。

[ 処理の流れ ]
1. 構文解析(Parsing):SQLの文法に誤りがないかチェックし、意味を解析します。
2. 実行計画の作成(Optimization):インデックスを使うか、どの結合法を選ぶかなど、最も効率的なルートを計画します。
3. コンパイル:作成された計画を、データベースが実行可能な形式に変換します。
4. SQLの実行:実際にデータへアクセスし、結果を返します。

2. 各フェーズで何が行われているか

各ステップの詳細を見てみましょう。エンジニアが意識すべきは、特に「2」のプロセスです。

構文解析の壁:キーワードの綴りミスや、存在しないテーブルの指定などはここでエラーとなります。
オプティマイザの判断:統計情報を元に「実行計画(実行の設計図)」を作ります。ここでの判断ミスがスロークエリの原因になります。
キャッシュの活用:一度解析したSQLは「共有プール」に保存され、2回目以降はステップ1〜3を省略して高速化(ソフトパース)される仕組みがあります。

3. まとめ:実行ステップを意識するメリット

「なぜバインド変数を使うべきなのか」「なぜ統計情報の更新が必要なのか」といった疑問の答えは、すべてこの実行ステップの中にあります。

効率化のコツ
・バインド変数を使うことで、再解析(ハードパース)のコストを減らせる。
・最新の統計情報を保つことで、最適な実行計画を立てさせることができる。
・複雑なクエリは、実行計画を確認(EXPLAIN)してボトルネックを特定する。


SQLをただの「命令文」としてではなく、DB内部の「プロセス」として捉えることが、脱・初心者への近道です。


【データベースの知識】MVCCとロックの関係


データベースの並行性を高める技術「MVCC(Multi-Version Concurrency Control)」。これによって「読み取りと書き込みがブロックされない」仕組みが実現されていますが、すべてのロックが不要になるわけではありません。

1. 更新時の動作とロックの役割

【 基本 】 MVCC環境下でも、データの更新(UPDATE/DELETE)時には「排他ロック」を取得します。これは、複数のトランザクションが同時に同じ行を書き換えてしまう「ロストアップデート」を防ぐためです。

[ 更新のポイント ]
更新時のロック:対象行に対して排他ロックを取得します。原則として「行レベル」でロックされます。
更新 vs 更新:同じ行を更新しようとすると、先行する処理が終わるまで「ロック待ち」が発生します。
必ず行ロックか?:通常は行単位ですが、大量の行を一度に更新する場合やインデックス再構築時には、効率化のため「テーブルロック」へ格上げ(エスカレーション)されることがあります。

2. 「読取り」と「書込み」の関係

【 基本 】 MVCCの最大のメリットは、読み取り(SELECT)がロックを取得しないことです。書き込み中であっても、読み取り側は「その処理が始まる前の古いバージョン(多版)」を参照するため、互いに待ち時間が発生しません。

[ ロックの競合 ]
更新 vs 読み込み:ロック待ちなし(読取り側は古い版を見る)。
読み込み vs 更新:ロック待ちなし。
参照一貫性:スナップショットを見ることで、長時間実行される検索も一貫性が保たれます。

3. まとめ:MVCCとロックの整理

質問の答えを整理すると、以下のようになります。この仕組みにより、高いスループットが実現されています。

[ Q&A 形式での整理 ]
1. 更新の際にロックは取得するか?
→ はい。整合性を守るために必須です。原則として「行ロック」がかかります。
2. 更新と読み込みでロック待ちはあるか?
→ いいえ。MVCCにより、読み取りはロックを無視して実行可能です。
3. 更新と更新でロック待ちは発生するか?
→ はい。同一データへの同時書き込みは、必ず順番待ちが発生します。


基本は「行ロック」ですが、処理の規模によっては「テーブルロック」になる可能性も意識しておくと、より深いデータベース設計が可能になります。


【データベースの知識】直列可能性(Serializability)と線形可能性(Linearizability)


一貫性のあるデータ操作を理解する上で、「直列可能性」と「線形可能性」は非常に重要な概念です。どちらも「正しく動く」ことを保証するためのルールですが、その焦点は異なります。

1. 直列可能性(Serializability)

【 基本 】 複数のトランザクションを並列に実行した結果が、それらを「何らかの順序で一つずつ順番に(直列に)実行した結果」と同じになることを保証します。これは「トランザクション」の境界を守るための概念です。

[ 直列可能性のイメージ ]
T1: [---A---]
T2: [---B---]
並列実行しても、結果が「T1 → T2」または「T2 → T1」の実行結果と一致すればOK。
焦点:複数操作(トランザクション)の整合性
範囲:データベース全体の整合性

2. 線形可能性(Linearizability)

【 基本 】 単一のオブジェクトに対する操作が、「ある一瞬」で完了したかのように見えることを保証します。書き込みが完了した瞬間、その後の読み込みは必ず最新の値を返す必要があります。「リアルタイム性」を重視する概念です。

[ 線形可能性のイメージ ]
書き込み: [--- Write(X=10) ---]
読み込み: [--- Read(X) ---] → 必ず 10 が返る
焦点:単一オブジェクト、単一操作の最新性
範囲:時間軸に沿った最新情報の提供

3. まとめ:違いのポイント

この二つは独立した概念ですが、組み合わせて「厳密な直列可能性(Strict Serializability)」として実装されることもあります。

直列可能性:並列実行しても、順番にやったのと同じ結果になる(マルチステップ重視)
線形可能性:操作が完了した瞬間に全員が最新値を見れる(時間的即時性重視)


システムの要件に応じて、どのレベルの整合性が必要かを見極めることが、堅牢なシステム設計の第一歩となります。


        
  • 1
  • 2