Oracle データベースを長期間運用すると、大量の DELETE や UPDATE によってテーブル内に無駄な空き領域が蓄積し、表領域内の空きエクステントが分散します。これが断片化であり、フルスキャンの性能低下やディスクの無駄遣いの原因になります。
本記事では、2 種類の断片化(テーブル断片化 / 表領域断片化)の違い、断片化の診断 SQL、ALTER TABLE MOVE / SHRINK SPACE / COALESCEによる再編成手順まで解説します。
この記事でわかること
・テーブル断片化(HWM 問題)と表領域断片化の違い
・断片化を診断する SQL(HWM / 実データ量の乖離検出)
・ALTER TABLE MOVE による再編成
・SHRINK SPACE によるオンライン圧縮
・ALTER TABLESPACE COALESCE による空きエクステント結合
・インデックスの再構築(REBUILD)
・12c MOVE ONLINE(ダウンタイムなし再編成)
・LOB セグメントの断片化対策
・テーブル断片化(HWM 問題)と表領域断片化の違い
・断片化を診断する SQL(HWM / 実データ量の乖離検出)
・ALTER TABLE MOVE による再編成
・SHRINK SPACE によるオンライン圧縮
・ALTER TABLESPACE COALESCE による空きエクステント結合
・インデックスの再構築(REBUILD)
・12c MOVE ONLINE(ダウンタイムなし再編成)
・LOB セグメントの断片化対策
2 種類の断片化
| 種類 | 発生場所 | 原因 | 影響 | 解消方法 |
|---|---|---|---|---|
| テーブル断片化 (HWM 問題) |
テーブル内部 | DELETE で行が消えても HWM(High Water Mark)は下がらない | フルテーブルスキャンが遅い(空ブロックも読む) | ALTER TABLE MOVE / SHRINK SPACE |
| 表領域断片化 (空きエクステント分散) |
表領域全体 | テーブルの作成・削除が繰り返され空きが分散 | 大きなエクステントを確保できず ORA-01653 | COALESCE / テーブル MOVE |
HWM(High Water Mark)とは
テーブルが「これまでに使用した最大ブロック位置」のマークです。DELETE で行を削除しても HWM は下がりません。フルテーブルスキャンは HWM までの全ブロックを読むため、大量 DELETE 後のテーブルはデータ量が少ないのにスキャンが遅いという症状が出ます。
テーブルが「これまでに使用した最大ブロック位置」のマークです。DELETE で行を削除しても HWM は下がりません。フルテーブルスキャンは HWM までの全ブロックを読むため、大量 DELETE 後のテーブルはデータ量が少ないのにスキャンが遅いという症状が出ます。
断片化の診断 SQL
テーブル断片化(HWM と実データ量の乖離)
SQL(テーブルの無駄領域を検出)
-- セグメントサイズ(HWM まで)と実際の行数から無駄を推定
SELECT t.table_name,
ROUND(s.bytes/1024/1024, 1) AS segment_mb,
t.num_rows,
t.avg_row_len,
ROUND(t.num_rows * t.avg_row_len / 1024 / 1024, 1) AS est_data_mb,
ROUND(s.bytes/1024/1024 - t.num_rows * t.avg_row_len / 1024 / 1024, 1) AS wasted_mb
FROM user_tables t
JOIN user_segments s ON t.table_name = s.segment_name AND s.segment_type = 'TABLE'
WHERE t.num_rows > 0
AND s.bytes > t.num_rows * t.avg_row_len * 3 -- セグメントが推定の 3 倍以上
ORDER BY wasted_mb DESC;
-- ※ num_rows / avg_row_len は統計情報ベース
-- 正確にするには DBMS_STATS.GATHER_TABLE_STATS を先に実行
SQL(DBMS_SPACE で正確な空き領域を確認)
-- テーブルの空きブロック数を正確に取得
DECLARE
v_unformatted_blocks NUMBER;
v_unformatted_bytes NUMBER;
v_fs1_blocks NUMBER; v_fs1_bytes NUMBER;
v_fs2_blocks NUMBER; v_fs2_bytes NUMBER;
v_fs3_blocks NUMBER; v_fs3_bytes NUMBER;
v_fs4_blocks NUMBER; v_fs4_bytes NUMBER;
v_full_blocks NUMBER; v_full_bytes NUMBER;
BEGIN
DBMS_SPACE.SPACE_USAGE(
segment_owner => USER,
segment_name => 'EMPLOYEES',
segment_type => 'TABLE',
unformatted_blocks => v_unformatted_blocks,
unformatted_bytes => v_unformatted_bytes,
fs1_blocks => v_fs1_blocks, fs1_bytes => v_fs1_bytes,
fs2_blocks => v_fs2_blocks, fs2_bytes => v_fs2_bytes,
fs3_blocks => v_fs3_blocks, fs3_bytes => v_fs3_bytes,
fs4_blocks => v_fs4_blocks, fs4_bytes => v_fs4_bytes,
full_blocks => v_full_blocks, full_bytes => v_full_bytes
);
DBMS_OUTPUT.PUT_LINE('Full blocks: ' || v_full_blocks);
DBMS_OUTPUT.PUT_LINE('0-25% free blocks: ' || v_fs1_blocks);
DBMS_OUTPUT.PUT_LINE('25-50% free blocks: ' || v_fs2_blocks);
DBMS_OUTPUT.PUT_LINE('50-75% free blocks: ' || v_fs3_blocks);
DBMS_OUTPUT.PUT_LINE('75-100% free blocks:' || v_fs4_blocks);
DBMS_OUTPUT.PUT_LINE('Unformatted: ' || v_unformatted_blocks);
END;
/
-- fs3/fs4/unformatted が多い = 断片化が激しい
表領域断片化(空きエクステントの分散)
SQL(空きエクステントの断片化)
-- 空きエクステント数と最大連続空き
SELECT tablespace_name,
COUNT(*) AS free_extents,
ROUND(SUM(bytes)/1024/1024, 1) AS total_free_mb,
ROUND(MAX(bytes)/1024/1024, 1) AS max_contiguous_mb
FROM dba_free_space
GROUP BY tablespace_name
HAVING COUNT(*) >= 20 -- 20 個以上の断片 = 要注意
ORDER BY free_extents DESC;
DBA_FREE_SPACE の詳細は「DBA_FREE_SPACE で空き容量を調べる方法」を参照してください。
ALTER TABLE MOVE(テーブルの再編成)
テーブルのデータを新しいブロックに再配置し、HWM をリセットします。最も効果的な断片化解消方法です。
SQL(ALTER TABLE MOVE 基本)
-- 同じ表領域内で再配置 ALTER TABLE hr.employees MOVE; -- 別の表領域に移動しながら再編成 ALTER TABLE hr.employees MOVE TABLESPACE new_data; -- MOVE 後は全インデックスが UNUSABLE → 再構築が必須 ALTER INDEX hr.idx_emp_dept REBUILD; ALTER INDEX hr.pk_employees REBUILD;
SQL(12c 以降: MOVE ONLINE でダウンタイムなし)
-- MOVE ONLINE: DML をブロックせずに再編成(12c 以降) ALTER TABLE hr.employees MOVE ONLINE; -- MOVE ONLINE ではインデックスも自動的にメンテナンスされる -- → REBUILD 不要(12c 以降の大きなメリット)
| 方式 | DML 中の実行 | インデックス | バージョン |
|---|---|---|---|
| ALTER TABLE MOVE | 不可(テーブルロック) | UNUSABLE(REBUILD 必要) | 全バージョン |
| ALTER TABLE MOVE ONLINE | 可能(DML 継続) | 自動メンテナンス(REBUILD 不要) | 12c 以降 |
MOVE 後はインデックスの再構築が必須(11g 以前)
ALTER TABLE MOVE で ROWID が変わるため、テーブル上の全インデックスが UNUSABLE になります。MOVE 後に
ALTER TABLE MOVE で ROWID が変わるため、テーブル上の全インデックスが UNUSABLE になります。MOVE 後に
ALTER INDEX ... REBUILD を必ず実行してください。12c の MOVE ONLINE ではインデックスも自動的に更新されるため REBUILD は不要です。SQL(テーブルの全インデックスを一括 REBUILD)
-- テーブルの全インデックスを一括再構築
BEGIN
FOR rec IN (
SELECT index_name FROM user_indexes
WHERE table_name = 'EMPLOYEES'
) LOOP
EXECUTE IMMEDIATE 'ALTER INDEX ' || rec.index_name || ' REBUILD';
END LOOP;
END;
/
SHRINK SPACE(オンライン圧縮)
SHRINK SPACE はテーブルの行を前方のブロックに移動し、HWM を下げて末尾の空き領域を解放します。MOVE と異なりオンライン(DML 継続中)で実行できます。
SQL(SHRINK SPACE)
-- (1) 行移動を有効化(初回のみ) ALTER TABLE hr.employees ENABLE ROW MOVEMENT; -- (2) SHRINK SPACE 実行 ALTER TABLE hr.employees SHRINK SPACE; -- CASCADE: テーブル + 全インデックスを同時に圧縮 ALTER TABLE hr.employees SHRINK SPACE CASCADE; -- COMPACT: 行の移動だけ実行(HWM は後で下げる = 2 段階) ALTER TABLE hr.employees SHRINK SPACE COMPACT; -- 後日: HWM を下げる ALTER TABLE hr.employees SHRINK SPACE;
| オプション | 動作 | ロック |
|---|---|---|
| SHRINK SPACE | 行を前方に移動 + HWM を下げる | 短時間のロック(HWM 変更時のみ) |
| SHRINK SPACE CASCADE | テーブル + 全インデックスを同時に圧縮 | 短時間のロック |
| SHRINK SPACE COMPACT | 行の移動だけ(HWM は後で下げる) | ロックなし |
MOVE vs SHRINK SPACE の使い分け
・MOVE: 効果が大きい(完全に再配置)。11g ではロックが発生。12c MOVE ONLINE ならロックなし
・SHRINK SPACE: オンラインで実行可能だが効果は MOVE より小さい場合がある
・12c 以降なら MOVE ONLINE が最も推奨(効果大 + ロックなし + REBUILD 不要)
・MOVE: 効果が大きい(完全に再配置)。11g ではロックが発生。12c MOVE ONLINE ならロックなし
・SHRINK SPACE: オンラインで実行可能だが効果は MOVE より小さい場合がある
・12c 以降なら MOVE ONLINE が最も推奨(効果大 + ロックなし + REBUILD 不要)
ALTER TABLESPACE COALESCE
SQL(COALESCE: 表領域の空きエクステントを結合)
-- 隣接する空きエクステントを結合
ALTER TABLESPACE users COALESCE;
-- COALESCE 前後の比較
SELECT tablespace_name, COUNT(*) AS free_extents,
ROUND(MAX(bytes)/1024/1024, 1) AS max_contiguous_mb
FROM dba_free_space
WHERE tablespace_name = 'USERS'
GROUP BY tablespace_name;
COALESCE は「隣接する」空きエクステントのみ結合
テーブルデータに挟まれた空きは結合できません。根本的に断片化を解消するにはテーブルの MOVE で再配置する必要があります。COALESCE は「ORA-01653 の応急処置」として使い、恒久対策は MOVE で行うのが定石です。
テーブルデータに挟まれた空きは結合できません。根本的に断片化を解消するにはテーブルの MOVE で再配置する必要があります。COALESCE は「ORA-01653 の応急処置」として使い、恒久対策は MOVE で行うのが定石です。
インデックスの断片化と再構築
SQL(インデックスの断片化を確認)
-- ANALYZE INDEX で断片化を確認
ANALYZE INDEX hr.idx_emp_dept VALIDATE STRUCTURE;
-- INDEX_STATS で削除率を確認
SELECT name, height, lf_rows, del_lf_rows,
ROUND(del_lf_rows / NULLIF(lf_rows, 0) * 100, 1) AS del_pct
FROM index_stats;
-- del_pct が 20% 以上なら REBUILD を検討
SQL(インデックスの再構築)
-- 基本 ALTER INDEX hr.idx_emp_dept REBUILD; -- ONLINE: DML をブロックせずに再構築 ALTER INDEX hr.idx_emp_dept REBUILD ONLINE; -- NOLOGGING + PARALLEL で高速再構築(メンテナンス時間帯) ALTER INDEX hr.idx_emp_dept REBUILD NOLOGGING PARALLEL 4; ALTER INDEX hr.idx_emp_dept LOGGING; ALTER INDEX hr.idx_emp_dept NOPARALLEL;
インデックスの詳細は「インデックスの作成・再構築・削除完全ガイド」を参照してください。
LOB セグメントの断片化対策
SQL(LOB セグメントの断片化確認と再編成)
-- LOB セグメントのサイズ確認
SELECT l.table_name, l.column_name, l.segment_name,
ROUND(s.bytes/1024/1024, 1) AS lob_mb
FROM user_lobs l
JOIN user_segments s ON l.segment_name = s.segment_name
ORDER BY s.bytes DESC;
-- LOB の再編成: ALTER TABLE MOVE で LOB も一緒に移動
ALTER TABLE hr.documents MOVE
LOB (content) STORE AS (TABLESPACE lob_data);
-- 12c 以降: MOVE ONLINE
ALTER TABLE hr.documents MOVE ONLINE
LOB (content) STORE AS (TABLESPACE lob_data);
LOB セグメントは ALTER TABLE MOVE でしか再編成できない
SHRINK SPACE は通常のテーブルデータには有効ですが、SecureFiles LOB 以外の LOB セグメント(BasicFiles LOB)には効果がありません。LOB の断片化を解消するには ALTER TABLE MOVE を使ってください。
SHRINK SPACE は通常のテーブルデータには有効ですが、SecureFiles LOB 以外の LOB セグメント(BasicFiles LOB)には効果がありません。LOB の断片化を解消するには ALTER TABLE MOVE を使ってください。
断片化解消の完全な手順
| # | 手順 | SQL |
|---|---|---|
| 1 | 統計情報を更新 | EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, ‘TABLE_NAME’) |
| 2 | 断片化の診断(無駄領域の確認) | 前述の診断 SQL で segment_mb vs est_data_mb を比較 |
| 3 | 再編成の実行 | ALTER TABLE t MOVE ONLINE(12c+)/ SHRINK SPACE CASCADE |
| 4 | インデックスの再構築(MOVE の場合) | ALTER INDEX idx REBUILD(MOVE ONLINE なら不要) |
| 5 | 統計情報を再収集 | EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, ‘TABLE_NAME’) |
| 6 | 効果の確認 | セグメントサイズの削減を確認 |
SQL(再編成の効果を確認)
-- 再編成前後のセグメントサイズを比較
SELECT segment_name, segment_type,
ROUND(bytes/1024/1024, 1) AS size_mb
FROM user_segments
WHERE segment_name = 'EMPLOYEES';
-- 再編成前: 500 MB → 再編成後: 150 MB = 350 MB 削減
実務パターン集
パターン(1): 大量 DELETE 後のテーブルを圧縮(12c+)
SQL
-- 大量 DELETE でデータが 1/10 になったテーブルを再編成
ALTER TABLE hr.audit_log MOVE ONLINE;
-- インデックスも自動メンテナンス(12c MOVE ONLINE)
-- 統計再収集
EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'AUDIT_LOG');
パターン(2): 全テーブルを一括再編成(11g)
SQL
-- 断片化が激しいテーブルを一括 MOVE + REBUILD
BEGIN
FOR rec IN (
SELECT t.table_name
FROM user_tables t
JOIN user_segments s ON t.table_name = s.segment_name
WHERE s.segment_type = 'TABLE'
AND t.num_rows > 0
AND s.bytes > t.num_rows * t.avg_row_len * 3
) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || rec.table_name || ' MOVE';
FOR idx IN (
SELECT index_name FROM user_indexes
WHERE table_name = rec.table_name
) LOOP
EXECUTE IMMEDIATE 'ALTER INDEX ' || idx.index_name || ' REBUILD';
END LOOP;
END LOOP;
END;
/
パターン(3): SHRINK SPACE をオンラインで実行
SQL
-- 業務時間中にオンラインで圧縮 ALTER TABLE hr.orders ENABLE ROW MOVEMENT; ALTER TABLE hr.orders SHRINK SPACE CASCADE;
パターン(4): 表領域の断片化を COALESCE で応急処置
SQL
-- ORA-01653 が出た → COALESCE で応急処置 ALTER TABLESPACE users COALESCE; -- 効果確認 SELECT COUNT(*) AS free_extents, ROUND(MAX(bytes)/1024/1024, 1) AS max_mb FROM dba_free_space WHERE tablespace_name = 'USERS';
よくある質問
Q断片化はどのくらいの頻度で確認すべきですか?
A大量の DELETE / UPDATE が発生するテーブルは月次で確認することを推奨します。セグメントサイズが推定データ量の 3 倍以上になっていれば再編成を検討してください。変更が少ないテーブルは年次で十分です。
QALTER TABLE MOVE で空き容量が必要ですか?
Aはい。MOVE はテーブルを新しいブロックにコピーしてから古いブロックを解放するため、一時的にテーブルサイズ分の追加領域が必要です。表領域の空きが不足している場合は先にデータファイルを追加してください。
QSHRINK SPACE と MOVE はどちらが効果的ですか?
AMOVE の方が効果的です。MOVE はテーブル全体を再配置するため無駄が完全になくなります。SHRINK SPACE は行を前方に詰めるだけなので効果が限定的な場合があります。12c 以降は MOVE ONLINE が最も推奨です。
QSHRINK SPACE で ENABLE ROW MOVEMENT が必要なのはなぜですか?
ASHRINK SPACE は行を物理的に移動するため、ROWID が変わります。ENABLE ROW MOVEMENT は「行の物理移動を許可する」設定です。これがないと ORA-10636 エラーが出ます。
QMOVE ONLINE は Enterprise Edition 専用ですか?
A12c R2 以降の
ALTER TABLE MOVE ONLINE はEnterprise Edition 専用です。Standard Edition では通常の MOVE(テーブルロック発生)を使うか、SHRINK SPACE で対処してください。QTRUNCATE すれば断片化は解消しますか?
Aはい。
TRUNCATE TABLE は HWM をリセットし、全ブロックを解放するため断片化は完全に解消されます。ただしデータも全件消えるため、データを保持したまま断片化を解消するには MOVE / SHRINK SPACE を使います。まとめ
断片化の解消方法をまとめます。
| やりたいこと | 方法 |
|---|---|
| テーブルの断片化を診断 | user_tables.num_rows * avg_row_len vs user_segments.bytes を比較 |
| テーブルを再編成(11g) | ALTER TABLE t MOVE + ALTER INDEX idx REBUILD |
| テーブルを再編成(12c+ 推奨) | ALTER TABLE t MOVE ONLINE(ロックなし + REBUILD 不要) |
| オンラインで圧縮 | ENABLE ROW MOVEMENT + SHRINK SPACE CASCADE |
| 表領域の空きエクステントを結合 | ALTER TABLESPACE ts COALESCE |
| インデックスの断片化を解消 | ALTER INDEX idx REBUILD [ONLINE] |
| LOB の断片化を解消 | ALTER TABLE t MOVE LOB (col) STORE AS (…) |
空きエクステントの分析は「DBA_FREE_SPACE で空き容量を調べる方法」、インデックスの詳細は「インデックスの作成・再構築・削除完全ガイド」、表領域全般は「表領域(Tablespace)完全ガイド」も併せて参照してください。

