UPDATE文の処理が急に遅くなり、「実行計画も問題なさそうなのに原因が分からない」という状況に遭遇した経験はないでしょうか。
筆者も実務で、大量データ更新のバッチ処理が想定の数倍以上の時間を要し、SQLチューニングやサーバ性能を疑ったものの改善しなかったことがあります。
最終的な原因は「INDEXの存在」でした。
SELECTでは高速化に貢献するINDEXですが、UPDATE処理では逆にパフォーマンス低下の原因になるケースがあります。本記事では、UPDATE処理が遅くなる本当の理由をデータベース内部の動作から解説します。
UPDATE処理が遅くなる主な原因とは
UPDATEが遅くなる原因は複数ありますが、実務で最も見落とされやすいのがINDEX更新コストです。
代表的な原因を整理すると以下の通りです。
| 原因 | 内容 |
|---|---|
| INDEX更新処理 | 更新時にINDEXも書き換えが発生 |
| 行ロック競合 | 同時更新による待機 |
| REDOログ増加 | 更新量増大によるI/O負荷 |
| テーブル断片化 | 更新によるブロック分散 |
| 不要INDEX過多 | 更新対象外でも更新対象になる |
特に重要なのがINDEX更新処理です。
UPDATE性能の低下はINDEX数だけでなく、「INDEXが正しく利用されているか」も重要なポイントです。
SQLの書き方によってはINDEXが存在していても使用されず、想定外の性能劣化を引き起こす場合があります。
INDEXが効かない代表的な原因(LIKE検索・関数使用・暗黙的変換)については、以下の記事で詳しく解説しています。

UPDATE時にINDEXでは何が起きているのか

多くの人が誤解していますが、UPDATEは単純な「値の変更」ではありません。
データベース内部では以下の処理が実行されています。
- 対象行を検索
- データ行を書き換え
- INDEXエントリ削除
- INDEXエントリ再登録
- REDOログ記録
つまりUPDATEとは、
「DELETE + INSERT」に近い動作
をしています。
例えば以下のケースを考えます。
- テーブルにINDEXが5個存在
- UPDATE対象列がINDEX対象列
この場合、1行更新するたびに5個すべてのINDEX更新が発生します。
結果としてI/Oが急増し、処理時間が大幅に伸びます。
INDEXが多いほどUPDATEが遅くなる理由
INDEXはB-tree構造で管理されています。
UPDATE時には次の処理が発生します。
| 処理 | 内容 |
|---|---|
| 古いキー削除 | INDEXツリーから削除 |
| 新しいキー追加 | 再配置処理 |
| ノード分割 | ページ分裂発生 |
| ブロック書き込み | ディスクI/O増加 |
特に問題になるのがページ分割(Block Split)です。
INDEX領域に空きがない場合、新しいデータ配置のためにツリー構造の再編成が発生します。これが頻発するとUPDATE性能は急激に低下します。
SELECTとUPDATEのINDEX負荷比較
| 処理 | INDEX動作 |
|---|---|
| SELECT | 読み取りのみ |
| UPDATE | 削除+追加 |
| DELETE | INDEX削除 |
UPDATE対象列がINDEXに含まれると遅くなる
以下は非常に重要なポイントです。
| UPDATE対象 | INDEX影響 |
|---|---|
| INDEX未使用列 | INDEX更新なし |
| INDEX列 | INDEX再構築発生 |
| 複合INDEX列 | 全INDEX更新 |
| 主キー更新 | 最大負荷 |
特に主キーや検索頻度の高い列をUPDATEすると、ほぼ確実に性能劣化が発生します。
実務では「ステータス列」にINDEXを付けた結果、更新バッチが極端に遅くなるケースがよくあります。
実行計画では問題が見えない理由
UPDATE遅延の厄介な点は、実行計画に原因が現れにくいことです。
実行計画は主に以下を示します。
- 行取得方法
- INDEX使用有無
- JOIN方式
しかしINDEX更新コストは表示されません。
つまり、
SELECTは速い
実行計画も正常
それでもUPDATEだけ遅い
という現象が発生します。
これは設計問題でありSQL問題ではありません。
UPDATE性能を改善する具体的な対策
実務で効果が高い順に紹介します。
不要なINDEXを削除する
最も効果があります。
更新頻度が高いテーブルでは、
- 検索用INDEX
- 更新性能
のバランスが重要です。
使用されていないINDEXは削除候補です。
更新前にINDEXを一時削除する(大量更新)
大量UPDATEの場合は以下が有効です。
- INDEX削除
- UPDATE実行
- INDEX再作成
INDEX再作成はバルク処理となるため高速です。
UPDATEを分割実行する
一括更新はログ負荷を増大させます。
例:
- 1万件単位でUPDATE
- COMMITを分割
これだけで処理時間が改善する場合があります。
INDEX再構築を実施する
断片化が進んでいる場合は再構築が有効です。
代表例(Oracle):
|
1 |
ALTER INDEX インデックス名 REBUILD; |
断片化したINDEXはUPDATEコストを増大させます。
UPDATEとSELECTでINDEXの役割が逆になる理由
SELECTではINDEXは検索高速化のための存在です。
しかしUPDATEでは次のように役割が変わります。
| 処理 | INDEX効果 |
|---|---|
| SELECT | 高速化 |
| INSERT | やや負荷増 |
| UPDATE | 大きな負荷 |
| DELETE | INDEX削除負荷 |
つまりINDEXは「読む処理」には強く、「書く処理」には弱い構造です。
更新中心テーブルにINDEXを増やしすぎると、システム全体の性能低下につながります。
よくある質問(Q & A)
- INDEXがある方がUPDATEも速くなりませんか?
-
なりません。検索は速くなりますが、更新時にはINDEX更新処理が追加されるため遅くなります。
- INDEXが多いとどのくらい遅くなりますか?
-
INDEX数に比例して更新コストが増加します。実務では5〜10倍以上遅くなるケースもあります。
- UPDATEが急に遅くなった原因は何ですか?
-
新規INDEX追加、データ増加、INDEX断片化が代表的な原因です。
- UPDATE対象の列にINDEXが付いていなくても遅くなることはありますか?
-
はい、あります。UPDATE対象列自体にINDEXがなくても、その行を特定する検索条件にINDEXが使用されていない場合、全表走査(TABLE ACCESS FULL)が発生し処理時間が増加します。また、同一テーブルに多数のINDEXが存在する場合は、更新対象外のINDEXでも内部整合性維持のため処理負荷が発生することがあります。
- UPDATEが遅い場合、まず確認すべきポイントは何ですか?
-
以下の順番で確認すると原因を特定しやすくなります。
確認項目 チェック内容 INDEX数 不要なINDEXが増えていないか 更新列 INDEX対象列を更新していないか 実行計画 TABLE ACCESS FULLになっていないか 更新件数 一括大量更新になっていないか INDEX断片化 REBUILDが必要な状態でないか 特に「最近INDEXを追加していないか」は実務で非常に多い原因です。
まとめ
UPDATE処理が遅くなる原因の多くはSQLそのものではなくINDEX設計にあります。
INDEXは検索性能を向上させる重要な仕組みですが、更新処理では追加コストとして動作します。
特に更新頻度の高いテーブルでは、
- INDEXを増やしすぎない
- 更新列にINDEXを付与しない
- 定期的にINDEX状態を確認する
といった設計が重要になります。
UPDATEが遅い場合は、まずINDEX数と更新対象列の関係を確認することが根本的な改善につながります。
