「SQLチューニング」タグアーカイブ

SQL:実行計画(EXPLAIN PLAN)の読み方とボトルネックの見つけ方

データベースチューニングにおいて「どのSQLが遅いのか」だけでなく、「どの処理がボトルネックなのか」を正しく把握することは非常に重要です。
そのための基本ツールが**実行計画(EXPLAIN PLAN)**です。

本記事では、Oracleを例に実行計画の見方とボトルネックの探し方を、初心者でも理解できるように解説します。


✅ EXPLAIN PLANとは?

SQLを実行する際、Oracleが内部的に考える**最適な実行手順(アクセス方法)**を表示する機能です。

実行計画を見ることで、次のようなことがわかります。

  • テーブルにアクセスする順番

  • インデックスを使っているかどうか

  • 結合(JOIN)の方式

  • フルスキャンが走っているか

  • コスト(予測負荷)


✅ 実行計画の取得方法

▼ 方法1:EXPLAIN PLAN文を使う

▼ 方法2:SQL Developer で「実行計画」ボタン

GUI環境ではワンクリックで参照できます。


✅ Oracle実行計画の基本構造

実行計画は階層構造で、上から順に処理が行われます。
インデントが深いほど「その処理の中で実行される詳細処理」です。

例:


✅ よく出るOperationと解釈ポイント

Operation説明見どころ
TABLE ACCESS FULLテーブルの全件スキャン大量データで出たら要注意
TABLE ACCESS BY INDEX ROWIDインデックス参照後にROWIDアクセス最適パターンの一つ
INDEX UNIQUE SCAN主キー・ユニークインデックス検索高速
INDEX RANGE SCAN範囲検索効率的だが条件次第
HASH JOINハッシュ表でJOIN大量データ向き、メモリ消費
NESTED LOOPS小規模データに適したJOIN結合相手の行数が多いと遅い
SORT ORDER BY並び替え必要ならOK、無駄がないか確認

✅ ボトルネックの探し方

① TABLE ACCESS FULL に注意

  • 条件にインデックスが効いていない可能性

  • 大規模テーブルで特に危険

対策:

  • WHERE句に使う列にインデックス追加

  • 不必要なSELECT *を避ける

  • ファンクションインデックス


② JOIN方式を確認

JOIN方式特徴適したケース
NESTED LOOPS小テーブル to 大テーブルに◎OLTP向き
HASH JOIN大量データ向き、高速DWH向き
MERGE JOIN並び替え前提、ソート負荷ソート後結合

Nested Loops × 大量データ → 遅い可能性


③ コスト(COST)とROWSを確認

項目意味
ROWS見積もられる行数
COSTOracleが見積もる負荷指数
BYTESデータ量

COSTが極端に高い行がボトルネック候補。


④ SORTが多い場合

ORDER BY や DISTINCTが多いと遅くなる

対策:

  • 必要な場面以外でDISTINCT使用しない

  • ORDER BYの列にインデックス


✅ 実例:遅いSQLの典型パターン

問題点

  • UPPER(ENAME) → 関数でインデックス無効

  • LIKE ‘%〇〇’ → 前方ワイルドカードでインデックス無効

  • SELECT * → 不要な列読み込み

改善例


✅ チューニングの基本手順まとめ

ステップ内容
1実行計画を見る
2TABLE FULL SCANをチェック
3JOIN方法確認(Nested Loops vs Hash Join)
4コスト高い箇所を特定
5インデックス/SQL修正

✅ まとめ

  • EXPLAIN PLANはSQLの動作設計図

  • インデックス利用とJOIN方式を重視

  • FULL SCANと高コスト行は警戒

  • 必要な列だけ取得し、関数利用に注意

SQLチューニングは**「まず実行計画を見る」**がスタートです。
慣れるほど読み解きが早くなり、効率的な分析ができるようになります。

Oracle「ORA-01652: 一時セグメントを拡張できません」対処手順

Oracleデータベースで大規模なソート処理や集計を行うと、
次のようなエラーが発生することがあります。

ORA-01652: 一時セグメントを拡張できません

このエラーは、一時表領域(TEMP)の空き容量不足によって
SQL実行が中断されたことを意味します。
ここでは、その原因と具体的な対処手順をわかりやすく解説します。


⚠️ エラーの意味

「ORA-01652」は、OracleがSQL実行中に必要な一時領域を確保できなかったことを示すエラーです。
特に次のような処理で発生しやすくなります。

  • 大量データを扱う ORDER BY / GROUP BY / DISTINCT / JOIN

  • インデックス作成集計処理

  • TEMP表領域が固定サイズで、自動拡張が無効


🔍 主な原因

原因詳細
TEMP表領域の容量不足TEMPファイルのサイズが小さい
自動拡張が無効TEMPFILE の AUTOEXTEND が OFF
不要なセッションがTEMPを占有他ユーザーの大規模SQLがTEMPを使用中
SQLの非効率な実行計画インデックス未使用・結合順序の問題

🧭 対処手順

① TEMP表領域の使用状況を確認

現在の使用率を確認します。

または以下のSQLでも確認可能です。


② TEMPファイルを追加または拡張

TEMP領域が不足している場合、ファイルを追加します。

または新しいTEMPファイルを追加します。


③ 不要なセッションを確認・終了

一時領域を使い続けているセッションを確認します。

不要なセッションが確認できた場合は、管理者権限で終了します。


④ SQLをチューニングする

  • インデックスを利用する

  • 不要なDISTINCTやORDER BYを削除

  • 結合順序を見直す

  • 集計処理を段階的に分割

一時領域を減らすため、SQLの実行計画を確認します。


⑤ TEMP表領域を再作成(最終手段)

TEMPファイル破損や肥大化が疑われる場合は、
再作成を検討します。


🌟 まとめ

対処法内容
TEMP使用量確認V$TEMP_SPACE_HEADERやV$SORT_USAGEで確認
TEMP拡張ADD TEMPFILE または RESIZE
セッション整理不要なセッションをKILL
SQLチューニング実行計画の見直し
TEMP再作成最終手段として実施

📘 補足:再発防止策

  • 定期的に V$TEMP_SPACE_HEADER を監視

  • TEMP表領域は AUTOEXTEND ON を推奨

  • 大規模SQL実行前に EXPLAIN PLAN を確認

  • バッチ処理では 分割実行 を検討

MySQLで「Too many connections」エラーが出たときの原因と対処法

MySQLを運用していると、ある日突然アプリケーション側から「Too many connections」というエラーが返され、データベースに接続できなくなることがあります。これは利用者にとってはサイトやサービスが「停止状態」と同じであり、早急な対応が必要です。本記事では、このエラーの原因と具体的な対処方法を整理します。


「Too many connections」エラーとは?

MySQLには同時に接続できるクライアント数を制御する仕組みがあります。
max_connections というパラメータで上限値が決められており、この数を超える新規接続要求があった場合に 「Too many connections」 エラーが発生します。

  • 初期値:151(バージョンによって異なる)

  • 上限:OSやハードウェアのリソースに依存

つまり、データベースが過負荷状態になったサインと捉えることができます。


主な原因

1. 接続数の急増

一時的にアクセスが集中し、アプリケーションからの同時接続数が急増することで上限を超えてしまいます。

2. 接続のクローズ漏れ

アプリケーション側で 接続プールの管理不備close処理の抜け があると、不要な接続が残り続けます。

3. 長時間実行されるクエリ

重いSQLが大量に実行されると、処理待ちの接続が積み重なり、結果的に接続枠を圧迫します。

4. 不適切な設定

wait_timeoutinteractive_timeout の値が長すぎると、アイドル状態の接続が切断されずに残ってしまうことがあります。


対処法

1. 一時的な応急処置

まずはサービス復旧を優先します。
MySQLに管理者で接続できる場合、現在の接続状況を確認します。

不要な接続が溜まっている場合は、強制的に切断します。
 
KILL 接続ID;

どうしても管理者で接続できない場合は、MySQLサービスの再起動が必要になる場合もあります。
(※ただし根本解決にはならず、緊急回避策に過ぎません。)


2. 根本的な解決策

(1) max_connections を増やす

一時的なアクセス増に備えるために上限値を上げます。

永続化する場合は my.cnf に設定を追記します。
 

(2) 接続プールの導入・見直し

アプリケーションで コネクションプーリング を利用し、使い終わった接続は必ず解放するようにします。
JavaならHikariCP、PHPならPDOやmysqliの接続プールを利用するのが一般的です。

(3) クエリのチューニング

  • インデックスを適切に設定する

  • 不要なJOINやサブクエリを減らす

  • キャッシュを導入する

これにより接続が長時間占有されることを防ぎます。

(4) timeout の調整

不要な接続が残り続けないように、wait_timeout の値を短めに設定します。


再発防止のために

  • アクセスのピーク時を想定して性能テストを行う

  • アプリケーション側で接続管理を徹底する

  • 監視ツール(例:Zabbix, Prometheus, CloudWatchなど)で接続数を常時モニタリングする

これらを実施することで「Too many connections」エラーを未然に防ぐことができます。


まとめ

「Too many connections」エラーは単なる設定値不足ではなく、接続管理やクエリ設計の問題 が隠れていることが多いです。

  • 一時的には接続数の上限を増やす

  • 長期的にはアプリケーション側の接続管理やSQLチューニングを見直す

これらをバランスよく行うことで、安定したMySQL運用が可能になります。

PL/SQL:BULK COLLECTとFORALLを使った効率的な大量データ処理

Oracle PL/SQLを使って大量データを処理する際、1行ずつループして処理を行うとパフォーマンスが低下します。
このようなケースで活躍するのが BULK COLLECTFORALL です。

これらを活用することで、SQLとPL/SQL間のコンテキスト切り替えを最小限に抑え、大量データを効率的に処理できます。


BULK COLLECTとは?

BULK COLLECTは、複数行のデータを一括でコレクション(配列型変数)に格納する仕組みです。

基本構文

 
SELECT カラム名 BULK COLLECT INTO コレクション変数 FROM テーブル名 WHERE 条件;

使用例

✅ 通常のSELECT INTOでは1行しか取得できませんが、BULK COLLECTを使うと複数行をまとめて変数に格納できます。


FORALLとは?

FORALLは、コレクションに格納されたデータを使って一括処理(INSERT/UPDATE/DELETE)を行う構文です。

基本構文

 
FORALL インデックス IN コレクション.FIRST .. コレクション.LAST DML文;

使用例

FORループで1件ずつUPDATEするよりも大幅に高速化できます。

BULK COLLECTとFORALLを組み合わせる

実務では、BULK COLLECTで一括取得 → FORALLで一括更新/削除といった流れがよく使われます。

処理フロー例

  1. BULK COLLECTで対象データを配列に取得

  2. 配列の内容をFORALLで一括更新

  3. コミット

このように組み合わせることで、バッチ処理や大量データ更新におけるパフォーマンスを劇的に改善できます。


パフォーマンス比較

  • 従来のループ処理
    SQLとPL/SQL間で行き来が多くなり、数万件以上の処理では遅くなる

  • BULK COLLECT + FORALL
    コンテキストスイッチが最小化され、処理速度が数倍〜数十倍向上するケースもある


注意点

  • BULK COLLECTで一度に大量データを取得するとメモリ不足の可能性あり
    LIMIT句を組み合わせて分割取得が推奨

  • FORALLはDML専用(SELECTでは使えない)

  • 例外処理はSAVE EXCEPTIONSを付けて制御することも可能


まとめ

  • BULK COLLECT → 複数行を一括取得

  • FORALL → 複数行を一括処理

  • 大量データ処理では必須テクニック

  • メモリ管理や例外処理に注意しつつ使うと、バッチ処理の効率が大幅に改善


💡 実際のプロジェクトでは「数十万件以上のデータ更新」で特に効果が出やすいため、PL/SQLチューニングの定番として覚えておきましょう。