【Oracle】LOBセグメントの削除と表領域の解放方法|MOVE・SHRINK・PURGE完全解説

【Oracle】LOBセグメントの削除と表領域の解放方法|MOVE・SHRINK・PURGE完全解説 Oracle

Oracleデータベースを運用していると、「表領域の空きが足りない」「特定のセグメントが異常に大きい」という問題に遭遇することがあります。その原因として非常に多いのがLOBセグメントの肥大化です。SYS_LOBで始まる見慣れないセグメントが表領域の大部分を占有しているケースは、実務で頻繁に発生します。

この記事では、LOBセグメントの仕組み・確認方法・削除・再編成・表領域の解放まで、実務で必要な手順を体系的に解説します。DELETE しても領域が返却されない理由や、ALTER TABLE MOVE LOBSHRINK SPACEPURGE RECYCLEBINなど、状況に応じた適切な対処法を網羅します。

この記事で分かること

  • LOBセグメント(CLOB/BLOB/NCLOB)の仕組みと命名規則
  • DBA_SEGMENTS / DBA_LOBS でLOBサイズを確認するSQL
  • DELETE後に領域が解放されない理由と正しい解放手順
  • ALTER TABLE MOVE LOB によるLOBセグメントの再編成
  • SHRINK SPACE によるオンライン縮小
  • DROP TABLE / PURGE RECYCLEBIN での完全削除
  • LOBセグメントを別表領域に移動するベストプラクティス
  • データファイルのRESIZEで表領域を縮小する方法
  • LOB肥大化の原因調査・バッチ削除・定期メンテナンス
スポンサーリンク
  1. LOBセグメントとは
    1. CLOB・BLOB・NCLOBの概要
    2. LOBセグメントが表領域を圧迫する理由
    3. SYS_LOB…OC…$$の命名規則
  2. LOBセグメントの確認方法
    1. DBA_SEGMENTS / USER_SEGMENTSでLOBセグメントのサイズ確認
    2. DBA_LOBS / USER_LOBSでテーブルとの紐付き確認
    3. LOBセグメントが大きいテーブルTOP10を取得するSQL
  3. LOBデータの削除と領域解放
    1. DELETEでのデータ削除(領域が解放されない問題)
    2. ALTER TABLE … MOVE LOB(LOBセグメントの再編成)
    3. SHRINK SPACE(オンライン縮小)
    4. ALTER TABLE MOVE + インデックス再構築
  4. テーブルごとDROPしてLOBセグメントを解放
    1. DROP TABLE … PURGE
    2. リサイクルビンのLOBセグメント(BIN$…)
    3. PURGE RECYCLEBINでの完全削除
  5. LOBセグメントの表領域を変更する
    1. ALTER TABLE … MOVE LOB (…) STORE AS (TABLESPACE 新表領域)
    2. LOBだけ別表領域に移動するベストプラクティス
  6. 表領域の空き容量を確認・解放する
    1. DBA_FREE_SPACEで空き確認
    2. ALTER DATABASE DATAFILE … RESIZEで縮小
    3. データファイル自動拡張の確認
  7. 実務でよく使うシナリオ
    1. LOB肥大化の原因調査手順
    2. 大量LOBデータの安全な削除(バッチ処理)
    3. LOBセグメントの定期メンテナンス
  8. よくあるミスと注意点
    1. DELETEしてもLOBの領域は返却されない
    2. MOVE後にインデックスが無効化される
    3. SHRINK SPACEが使えない条件(BASICFILE LOB)
    4. SecureFileとBasicFileの違い
  9. まとめ(手順フロー表)

LOBセグメントとは

LOB(Large Object)は、大量のデータを格納するためのOracleのデータ型です。テキストデータにはCLOB、バイナリデータにはBLOB、Unicode テキストにはNCLOBを使用します。LOBカラムのデータは、通常の行データとは別のセグメントに格納されます。これがLOBセグメントです。

CLOB・BLOB・NCLOBの概要

データ型 格納データ 最大サイズ 用途例
CLOB 文字データ (4GB – 1) x DB_BLOCK_SIZE 長文テキスト、XML、JSON
BLOB バイナリデータ (4GB – 1) x DB_BLOCK_SIZE 画像、PDF、ファイル
NCLOB Unicode文字データ (4GB – 1) x DB_BLOCK_SIZE 多言語テキスト

LOBセグメントが表領域を圧迫する理由

LOBカラムを含むテーブルを作成すると、Oracle は自動的にLOBセグメントLOBインデックスを作成します。通常の行データはテーブルセグメントに格納されますが、LOBデータはLOBセグメントに別途格納されます。

ポイント:LOBセグメントが肥大化する主な理由は次の3つです。(1) LOBカラムに大量のデータが蓄積される。(2) DELETE しても LOBセグメントの領域は自動的に解放されない。(3) UPDATE で LOBデータを更新すると、旧データの領域がそのまま残る(BASICFILE LOB の場合)。

このため、テーブルの行データは数MBなのに、LOBセグメントが数十GBに膨れ上がっているという状況が発生します。

SYS_LOB…OC…$$の命名規則

Oracle が自動生成するLOBセグメント名は、以下の命名規則に従います。

命名規則
-- LOBセグメント名の構造
SYS_LOB0000012345C00006$$

-- 各部分の意味
SYS_LOB     : LOBセグメントであることを示すプレフィックス
0000012345  : テーブルのOBJECT_ID(DBA_OBJECTSで確認可能)
C           : CLOBの場合はC、BLOBの場合もC
00006       : カラムの内部ID(INTCOL#$$          : サフィックス

LOBインデックスも同様に自動生成され、SYS_IL0000012345C00006$$のような名前になります。これらの名前からテーブルを特定するには、DBA_LOBSビューを使用します。

LOBセグメントの確認方法

LOBセグメントの問題を解決するには、まずどのLOBセグメントがどれくらいの領域を使っているかを正確に把握する必要があります。ここでは3つの確認方法を紹介します。

DBA_SEGMENTS / USER_SEGMENTSでLOBセグメントのサイズ確認

DBA_SEGMENTS(またはUSER_SEGMENTS)を使うと、LOBセグメントのサイズを直接確認できます。

SQL
-- LOBセグメントの一覧とサイズを確認
SELECT segment_name,
       segment_type,
       tablespace_name,
       ROUND(bytes / 1024 / 1024, 2) AS size_mb
FROM   dba_segments
WHERE  segment_type = 'LOBSEGMENT'
ORDER BY bytes DESC;
実行結果
SEGMENT_NAME                  SEGMENT_TYPE  TABLESPACE_NAME  SIZE_MB
--------------------------    ------------  ---------------  -------
SYS_LOB0000078523C00004$$     LOBSEGMENT    USERS            8,542.00
SYS_LOB0000065210C00003$$     LOBSEGMENT    USERS            3,128.50
SYS_LOB0000091045C00005$$     LOBSEGMENT    USERS            1,256.75

DBA権限がない場合は、USER_SEGMENTSを使用してください。自スキーマのセグメントのみ表示されます。

DBA_LOBS / USER_LOBSでテーブルとの紐付き確認

SYS_LOB...というセグメント名だけでは、どのテーブルのどのカラムのLOBなのか判別できません。DBA_LOBSを使って紐付きを確認します。

SQL
-- LOBセグメントとテーブル・カラムの対応を確認
SELECT l.owner,
       l.table_name,
       l.column_name,
       l.segment_name,
       l.tablespace_name,
       l.securefile
FROM   dba_lobs l
WHERE  l.owner = 'HR'
ORDER BY l.table_name, l.column_name;
実行結果
OWNER  TABLE_NAME      COLUMN_NAME  SEGMENT_NAME                  TABLESPACE  SECUREFILE
-----  --------------  -----------  ----------------------------  ----------  ----------
HR     DOCUMENTS       FILE_DATA    SYS_LOB0000078523C00004$$     USERS       NO
HR     DOCUMENTS       THUMBNAIL    SYS_LOB0000078523C00005$$     USERS       NO
HR     LOG_ENTRIES     LOG_TEXT     SYS_LOB0000065210C00003$$     USERS       YES

SECUREFILE列がYESの場合は SecureFile LOB、NOの場合は BasicFile LOB です。この違いは後述するSHRINK SPACEの可否に関わります。

LOBセグメントが大きいテーブルTOP10を取得するSQL

実務で最もよく使うのが、LOBセグメントのサイズでソートして上位のテーブルを特定するSQLです。DBA_SEGMENTSDBA_LOBSを結合して、テーブル名・カラム名・サイズを一覧表示します。

SQL
-- LOBセグメントが大きいテーブルTOP10
SELECT *
FROM (
  SELECT l.owner,
         l.table_name,
         l.column_name,
         l.segment_name,
         l.securefile,
         ROUND(s.bytes / 1024 / 1024, 2) AS lob_size_mb,
         ROUND(s.bytes / 1024 / 1024 / 1024, 2) AS lob_size_gb
  FROM   dba_lobs l
  JOIN   dba_segments s
    ON   s.owner = l.owner
    AND  s.segment_name = l.segment_name
  ORDER BY s.bytes DESC
)
WHERE ROWNUM <= 10;
実行結果
OWNER  TABLE_NAME       COLUMN_NAME  SEGMENT_NAME                  SEC  LOB_SIZE_MB  LOB_SIZE_GB
-----  ---------------  -----------  ----------------------------  ---  -----------  -----------
HR     DOCUMENTS        FILE_DATA    SYS_LOB0000078523C00004$$     NO     8,542.00         8.34
HR     AUDIT_LOG        LOG_DETAIL   SYS_LOB0000065210C00003$$     YES    3,128.50         3.06
APP    FILE_STORAGE     CONTENT      SYS_LOB0000091045C00005$$     NO     1,256.75         1.23
HR     DOCUMENTS        THUMBNAIL    SYS_LOB0000078523C00005$$     NO       845.25         0.83
APP    EMAIL_ARCHIVE    BODY_HTML    SYS_LOB0000088712C00006$$     YES      512.00         0.50

ポイント:このSQLを定期的に実行し、LOBセグメントのサイズ推移を監視することで、肥大化の早期検知が可能になります。特にSECUREFILENO(BasicFile)のLOBは領域管理が非効率なため、優先的に監視対象にしてください。

LOBデータの削除と領域解放

LOBセグメントの肥大化を解消するには、単にDELETEするだけでは不十分です。ここでは、LOBデータを削除して実際に領域を解放するための方法を、段階的に解説します。

DELETEでのデータ削除(領域が解放されない問題)

通常のDELETE文やEMPTY_LOB関数でLOBデータを削除しても、LOBセグメントの領域はOSに返却されません。これは Oracle の LOB 領域管理の仕様によるものです。

SQL
-- LOBデータを含む行を削除
DELETE FROM documents
WHERE created_date < TO_DATE('2023-01-01', 'YYYY-MM-DD');
COMMIT;

-- LOBカラムを空にする(行は残る)
UPDATE documents
SET    file_data = EMPTY_BLOB()
WHERE  doc_id = 100;
COMMIT;

注意:上記のDELETEやEMPTY_BLOB()を実行しても、DBA_SEGMENTSで確認するLOBセグメントのサイズはほとんど変わりません。削除されたデータの領域は「再利用可能な空き領域」としてLOBセグメント内部に残りますが、表領域やOSには返却されません。領域を実際に解放するには、次に説明するMOVEまたはSHRINKが必要です。

ALTER TABLE … MOVE LOB(LOBセグメントの再編成)

ALTER TABLE MOVE LOBは、LOBセグメントを再作成して使用中のデータだけを新しいセグメントにコピーします。これにより、未使用領域が除去され、LOBセグメントのサイズが大幅に縮小されます。

SQL
-- LOBセグメントを再編成(同じ表領域内で再作成)
ALTER TABLE documents
MOVE LOB (file_data) STORE AS (
  TABLESPACE users
);

複数のLOBカラムがある場合は、それぞれ個別に指定するか、テーブル全体をMOVEします。

SQL
-- 複数LOBカラムをまとめて再編成
ALTER TABLE documents
MOVE
  LOB (file_data) STORE AS (TABLESPACE users)
  LOB (thumbnail) STORE AS (TABLESPACE users);

注意:ALTER TABLE MOVEを実行すると、そのテーブルのすべてのインデックスが UNUSABLE(無効)状態になります。MOVE完了後に必ずインデックスを再構築してください(後述)。また、MOVE中はテーブルへのDML(INSERT/UPDATE/DELETE)がブロックされます。

SHRINK SPACE(オンライン縮小)

SHRINK SPACEは、テーブルやLOBセグメントの未使用領域をオンラインで縮小するコマンドです。MOVE と異なり、インデックスが無効化されないのが大きなメリットです。

SQL
-- 前提: 行移動を有効にする(未設定の場合)
ALTER TABLE documents ENABLE ROW MOVEMENT;

-- LOBセグメントを縮小
ALTER TABLE documents
MODIFY LOB (file_data) (
  SHRINK SPACE
);

SHRINK SPACE CASCADEを使うと、テーブル本体とLOBセグメントの両方を一度に縮小できます。

SQL
-- テーブル + LOBセグメントをまとめて縮小
ALTER TABLE documents SHRINK SPACE CASCADE;
比較項目 MOVE LOB SHRINK SPACE
オンライン実行 不可(DMLブロック) 可能
インデックスへの影響 UNUSABLE になる 影響なし
BasicFile LOB対応 対応 非対応
SecureFile LOB対応 対応 対応
ROW MOVEMENT必要 不要 必要
領域解放の効率 非常に高い 中程度

ALTER TABLE MOVE + インデックス再構築

ALTER TABLE MOVEを実行した後は、必ずインデックスを再構築する必要があります。以下は、無効化されたインデックスを確認して再構築する手順です。

SQL
-- Step 1: MOVE LOB を実行
ALTER TABLE documents
MOVE LOB (file_data) STORE AS (
  TABLESPACE users
);

-- Step 2: 無効化されたインデックスを確認
SELECT index_name,
       index_type,
       status
FROM   user_indexes
WHERE  table_name = 'DOCUMENTS'
AND    status = 'UNUSABLE';

-- Step 3: インデックスを再構築
ALTER INDEX pk_documents REBUILD;
ALTER INDEX idx_documents_date REBUILD;

対象テーブルのインデックスが多い場合は、PL/SQLで一括再構築すると効率的です。

PL/SQL
-- 無効インデックスを一括再構築
BEGIN
  FOR rec IN (
    SELECT index_name
    FROM   user_indexes
    WHERE  table_name = 'DOCUMENTS'
    AND    status = 'UNUSABLE'
  ) LOOP
    EXECUTE IMMEDIATE
      'ALTER INDEX ' || rec.index_name || ' REBUILD';
    DBMS_OUTPUT.PUT_LINE('Rebuilt: ' || rec.index_name);
  END LOOP;
END;
/

テーブルごとDROPしてLOBセグメントを解放

テーブル自体が不要であれば、DROP TABLEでテーブルとLOBセグメントを一括で削除するのが最も確実な方法です。ただし、リサイクルビンの動作に注意が必要です。

DROP TABLE … PURGE

DROP TABLEを実行すると、テーブル本体・LOBセグメント・LOBインデックス・通常のインデックスがすべて削除されます。PURGEを付けるとリサイクルビンを経由せず、即座に領域が解放されます。

SQL
-- テーブルをリサイクルビンに入れずに即削除
DROP TABLE documents PURGE;

-- 外部キー参照がある場合は CASCADE CONSTRAINTS を付ける
DROP TABLE documents CASCADE CONSTRAINTS PURGE;

ポイント:PURGEを付けた場合、FLASHBACK TABLE ... TO BEFORE DROPで復元することはできません。重要なデータがある場合は、DROP前に必ずバックアップを取得してください。

リサイクルビンのLOBセグメント(BIN$…)

PURGEを付けずにDROP TABLEした場合、テーブルはリサイクルビンに移動します。このとき、LOBセグメントもBIN$...という名前でリサイクルビン内に残り、表領域は解放されません

SQL
-- リサイクルビンの内容を確認
SELECT object_name,
       original_name,
       type,
       ts_name,
       ROUND(space * 8192 / 1024 / 1024, 2) AS size_mb
FROM   dba_recyclebin
WHERE  type IN ('TABLE', 'LOB', 'LOB INDEX')
ORDER BY droptime DESC;
実行結果
OBJECT_NAME                    ORIGINAL_NAME                  TYPE       TS_NAME  SIZE_MB
-----------------------------  -----------------------------  ---------  -------  -------
BIN$abc123==                   DOCUMENTS                      TABLE      USERS      12.50
BIN$def456==                   SYS_LOB0000078523C00004$$      LOB        USERS   8,542.00
BIN$ghi789==                   SYS_IL0000078523C00004$$       LOB INDEX  USERS       8.25

リサイクルビンのLOBセグメントが表領域の大部分を占有しているケースは非常に多いです。特に大きなLOBを持つテーブルを何度もDROP→再作成している環境では、この確認が重要です。

PURGE RECYCLEBINでの完全削除

SQL
-- 自スキーマのリサイクルビンを空にする
PURGE RECYCLEBIN;

-- 特定テーブルだけリサイクルビンから完全削除
PURGE TABLE documents;

-- DBA権限: 全ユーザーのリサイクルビンを空にする
PURGE DBA_RECYCLEBIN;

-- 特定表領域のリサイクルビンだけ空にする
PURGE TABLESPACE users;

注意:PURGE RECYCLEBINを実行すると、リサイクルビン内のすべてのオブジェクトが完全削除されます。FLASHBACK TABLEでの復元ができなくなるため、必要なテーブルがリサイクルビンに含まれていないか確認してから実行してください。

LOBセグメントの表領域を変更する

LOBセグメントが特定の表領域に集中して圧迫している場合、LOBセグメントだけを別の表領域に移動することで問題を解決できます。

ALTER TABLE … MOVE LOB (…) STORE AS (TABLESPACE 新表領域)

MOVE LOBで移動先の表領域を指定すると、LOBセグメントを別の表領域に移動できます。テーブル本体はそのままで、LOBだけを移動できるのがポイントです。

SQL
-- LOBセグメントを LOB_DATA 表領域に移動
ALTER TABLE documents
MOVE LOB (file_data) STORE AS (
  TABLESPACE lob_data
);

-- 移動後の確認
SELECT table_name,
       column_name,
       segment_name,
       tablespace_name
FROM   user_lobs
WHERE  table_name = 'DOCUMENTS';
実行結果
TABLE_NAME  COLUMN_NAME  SEGMENT_NAME                  TABLESPACE_NAME
----------  -----------  ----------------------------  ---------------
DOCUMENTS   FILE_DATA    SYS_LOB0000078530C00004$$     LOB_DATA
DOCUMENTS   THUMBNAIL    SYS_LOB0000078523C00005$$     USERS

LOBだけ別表領域に移動するベストプラクティス

大量のLOBデータを扱うシステムでは、LOB専用の表領域を作成してLOBセグメントを分離するのがベストプラクティスです。

SQL
-- Step 1: LOB専用の表領域を作成
CREATE TABLESPACE lob_data
DATAFILE '/u01/oradata/mydb/lob_data01.dbf'
SIZE 10G
AUTOEXTEND ON NEXT 1G
MAXSIZE 50G;

-- Step 2: 既存テーブルのLOBを移動
ALTER TABLE documents
MOVE LOB (file_data) STORE AS (TABLESPACE lob_data);

-- Step 3: インデックスを再構築
ALTER INDEX pk_documents REBUILD;

-- Step 4: 新規テーブル作成時はLOBの格納先を指定
CREATE TABLE new_documents (
  doc_id    NUMBER PRIMARY KEY,
  doc_name  VARCHAR2(200),
  file_data BLOB
)
TABLESPACE users
LOB (file_data) STORE AS SECUREFILE (
  TABLESPACE lob_data
  DISABLE STORAGE IN ROW
);
設計方針 メリット
LOB専用表領域を分離 テーブルデータとLOBデータのI/Oを分散できる
AUTOEXTEND ON を設定 LOB肥大化による表領域フルを防止できる
SECUREFILE を使用 圧縮・重複排除・SHRINK SPACEが使える
DISABLE STORAGE IN ROW すべてのLOBデータがLOBセグメントに格納される

表領域の空き容量を確認・解放する

LOBセグメントを削除・縮小した後、表領域に空き領域が生まれます。しかし、表領域の空きがあるだけではデータファイルのサイズは縮小されません。ここでは、空き容量の確認からデータファイルの縮小までの手順を解説します。

DBA_FREE_SPACEで空き確認

SQL
-- 表領域ごとの使用量・空き容量を確認
SELECT t.tablespace_name,
       ROUND(t.total_mb, 2)  AS total_mb,
       ROUND(f.free_mb, 2)   AS free_mb,
       ROUND(t.total_mb - f.free_mb, 2) AS used_mb,
       ROUND(f.free_mb / t.total_mb * 100, 1) AS free_pct
FROM   (
  SELECT tablespace_name,
         SUM(bytes) / 1024 / 1024 AS total_mb
  FROM   dba_data_files
  GROUP BY tablespace_name
) t
LEFT JOIN (
  SELECT tablespace_name,
         SUM(bytes) / 1024 / 1024 AS free_mb
  FROM   dba_free_space
  GROUP BY tablespace_name
) f
ON t.tablespace_name = f.tablespace_name
ORDER BY free_pct;
実行結果
TABLESPACE_NAME  TOTAL_MB    FREE_MB    USED_MB    FREE_PCT
---------------  ---------   --------   --------   --------
SYSTEM             800.00      45.00     755.00        5.6
USERS           20,480.00  12,800.00   7,680.00       62.5
LOB_DATA        10,240.00   9,500.00     740.00       92.8

ALTER DATABASE DATAFILE … RESIZEで縮小

表領域に大量の空きがあっても、データファイルのサイズは自動では縮小されません。ALTER DATABASE DATAFILE ... RESIZEでデータファイルを手動で縮小できます。

SQL
-- データファイルの現在のサイズと縮小可能サイズを確認
SELECT file_name,
       ROUND(bytes / 1024 / 1024, 2) AS current_mb,
       autoextensible
FROM   dba_data_files
WHERE  tablespace_name = 'USERS';

-- データファイルを縮小
ALTER DATABASE DATAFILE
  '/u01/oradata/mydb/users01.dbf'
  RESIZE 8G;

注意:データファイルは、最後に使用されているブロックよりも小さくRESIZEすることはできません。空き領域がファイルの末尾にない場合、ORA-03297: file contains used data beyond requested RESIZE valueエラーが発生します。この場合は、先にMOVEで末尾のセグメントを移動してからRESIZEしてください。

データファイル自動拡張の確認

LOBセグメントの肥大化を防ぐには、データファイルの自動拡張設定を適切に管理することが重要です。

SQL
-- 全データファイルの自動拡張設定を確認
SELECT tablespace_name,
       file_name,
       ROUND(bytes / 1024 / 1024, 2) AS current_mb,
       autoextensible,
       ROUND(maxbytes / 1024 / 1024, 2) AS max_mb,
       ROUND(increment_by * 8192 / 1024 / 1024, 2) AS increment_mb
FROM   dba_data_files
ORDER BY tablespace_name, file_name;

ポイント:自動拡張が有効(AUTOEXTENSIBLE = YES)でMAXSIZE UNLIMITEDに設定されている場合、LOBが際限なく拡張してディスクを消費する可能性があります。LOB専用表領域にはMAXSIZEの上限を必ず設定してください。

実務でよく使うシナリオ

ここでは、LOBセグメントの問題に直面したときの実務的な対処手順を、よくあるシナリオ別に紹介します。

LOB肥大化の原因調査手順

「表領域が逼迫している」というアラートが発生したとき、LOBセグメントが原因かどうかを特定する手順です。

SQL
-- Step 1: 表領域の使用率を確認
SELECT tablespace_name,
       ROUND(used_space * 8192 / 1024 / 1024, 2) AS used_mb,
       ROUND(tablespace_size * 8192 / 1024 / 1024, 2) AS total_mb,
       ROUND(used_percent, 1) AS used_pct
FROM   dba_tablespace_usage_metrics
ORDER BY used_pct DESC;

-- Step 2: 該当表領域のセグメントサイズTOP10
SELECT segment_name,
       segment_type,
       owner,
       ROUND(bytes / 1024 / 1024, 2) AS size_mb
FROM   dba_segments
WHERE  tablespace_name = 'USERS'
ORDER BY bytes DESC
FETCH FIRST 10 ROWS ONLY;

-- Step 3: LOBセグメントならテーブルを特定
SELECT l.table_name,
       l.column_name,
       l.segment_name,
       ROUND(s.bytes / 1024 / 1024, 2) AS lob_mb,
       (SELECT COUNT(*) FROM all_tables t
        WHERE t.owner = l.owner
        AND t.table_name = l.table_name) AS row_count_approx
FROM   dba_lobs l
JOIN   dba_segments s
  ON   s.segment_name = l.segment_name
  AND  s.owner = l.owner
WHERE  l.segment_name = 'SYS_LOB0000078523C00004$$';

大量LOBデータの安全な削除(バッチ処理)

LOBデータが大量にあるテーブルでは、一度にDELETEするとUNDO表領域を消費しすぎてエラーになります。バッチ処理で少しずつ削除するのが安全です。

PL/SQL
-- 1000件ずつバッチで削除
DECLARE
  v_rows  NUMBER;
  v_total NUMBER := 0;
BEGIN
  LOOP
    DELETE FROM documents
    WHERE  created_date < TO_DATE('2023-01-01', 'YYYY-MM-DD')
    AND    ROWNUM <= 1000;

    v_rows := SQL%ROWCOUNT;
    v_total := v_total + v_rows;
    COMMIT;

    DBMS_OUTPUT.PUT_LINE(
      'Deleted: ' || v_rows ||
      ' rows (Total: ' || v_total || ')'
    );

    EXIT WHEN v_rows = 0;

    -- UNDO表領域への負荷を軽減
    DBMS_LOCK.SLEEP(1);
  END LOOP;

  DBMS_OUTPUT.PUT_LINE(
    'Complete. Total deleted: ' || v_total || ' rows'
  );
END;
/

ポイント:バッチ削除完了後、LOBセグメントの領域はまだ解放されていません。削除後にALTER TABLE MOVE LOBまたはSHRINK SPACEを実行して、領域を実際に解放してください。

LOBセグメントの定期メンテナンス

LOBセグメントの肥大化を防ぐには、定期的なメンテナンスが重要です。以下のSQLをメンテナンスジョブに組み込むことで、LOBの肥大化を早期に検知できます。

SQL
-- LOBセグメントのサイズ推移を記録するテーブル
CREATE TABLE lob_size_history (
  check_date       DATE DEFAULT SYSDATE,
  owner            VARCHAR2(128),
  table_name       VARCHAR2(128),
  column_name      VARCHAR2(128),
  lob_size_mb      NUMBER
);

-- 日次で実行するサイズ記録SQL
INSERT INTO lob_size_history
  (owner, table_name, column_name, lob_size_mb)
SELECT l.owner,
       l.table_name,
       l.column_name,
       ROUND(s.bytes / 1024 / 1024, 2)
FROM   dba_lobs l
JOIN   dba_segments s
  ON   s.owner = l.owner
  AND  s.segment_name = l.segment_name
WHERE  s.bytes > 100 * 1024 * 1024;  -- 100MB以上のみ記録
COMMIT;

よくあるミスと注意点

LOBセグメントの管理では、知らないと痛い目を見る落とし穴がいくつかあります。ここでは、実務で特に注意すべきポイントをまとめます。

DELETEしてもLOBの領域は返却されない

これはLOBセグメント管理で最もよくある誤解です。通常のテーブルでは、DELETEした行の領域はHighWaterMark以下の空き領域として再利用されますが、LOBセグメントでは事情が異なります。

操作 テーブルセグメント LOBセグメント
DELETE HWM以下で再利用可能 セグメント内で再利用可能(縮小はしない)
TRUNCATE 領域が解放される 領域が解放される
MOVE 再編成(HWMリセット) 再編成(未使用領域除去)
SHRINK SPACE オンライン縮小 SecureFileのみ対応

MOVE後にインデックスが無効化される

ALTER TABLE MOVE(LOB MOVEを含む)を実行すると、テーブルのROWIDが変更されます。ROWIDに依存するインデックスはすべてUNUSABLE状態になり、そのまま放置するとSELECTやDMLでエラーが発生します。

エラー例
ORA-01502: index 'HR.PK_DOCUMENTS' or partition of such index is in unusable state

MOVE後は必ず以下を実行してください。

SQL
-- 無効インデックスの一覧を確認
SELECT index_name, status
FROM   user_indexes
WHERE  status = 'UNUSABLE';

-- 各インデックスをREBUILD
ALTER INDEX pk_documents REBUILD;
ALTER INDEX idx_documents_date REBUILD;

SHRINK SPACEが使えない条件(BASICFILE LOB)

SHRINK SPACEは便利なコマンドですが、BasicFile LOBでは使用できません。実行すると以下のエラーが発生します。

エラー例
ORA-10635: Invalid segment or tablespace type

LOBがBasicFileかSecureFileかを確認し、BasicFileの場合はMOVE LOBで対処してください。

SQL
-- LOBの種類を確認
SELECT table_name,
       column_name,
       securefile
FROM   user_lobs;
実行結果
TABLE_NAME      COLUMN_NAME  SECUREFILE
--------------  -----------  ----------
DOCUMENTS       FILE_DATA    NO          <-- BasicFile(SHRINK不可)
LOG_ENTRIES     LOG_TEXT     YES         <-- SecureFile(SHRINK可能)

SecureFileとBasicFileの違い

Oracle 11g以降ではSecureFile LOBが導入され、多くの点でBasicFile LOBより優れています。新規テーブルを作成する際は、SecureFileを使うことを推奨します。

機能 BasicFile LOB SecureFile LOB
圧縮 非対応 対応(LOW/MEDIUM/HIGH)
重複排除 非対応 対応(DEDUPLICATE)
暗号化 非対応 対応(ENCRYPT)
SHRINK SPACE 非対応 対応
パフォーマンス 従来型 大幅に改善
前提条件 なし ASSM表領域が必要
SQL
-- BasicFile LOB を SecureFile LOB に変換する
ALTER TABLE documents
MOVE LOB (file_data) STORE AS SECUREFILE (
  TABLESPACE lob_data
);

-- 変換後にインデックスを再構築
ALTER INDEX pk_documents REBUILD;

まとめ(手順フロー表)

LOBセグメントの削除と表領域の解放について解説しました。最後に、状況に応じた対処法の早見表を掲載します。

状況 対処法 注意点
LOBのサイズを確認したい DBA_SEGMENTS + DBA_LOBS を結合 DBA権限がなければ USER_ 系を使用
テーブルは残してLOBを縮小したい DELETE + MOVE LOB MOVE後にインデックスREBUILD必須
オンラインでLOBを縮小したい SHRINK SPACE SecureFile LOBのみ対応
テーブルごと不要 DROP TABLE ... PURGE PURGEを忘れるとリサイクルビンに残る
リサイクルビンが表領域を圧迫 PURGE RECYCLEBIN FLASHBACK復元不可になる
LOBを別表領域に移動したい MOVE LOB (...) STORE AS (TABLESPACE ...) 移動後にインデックスREBUILD必須
データファイルを縮小したい ALTER DATABASE DATAFILE ... RESIZE 末尾に使用中ブロックがあるとエラー
BasicFileをSecureFileにしたい MOVE LOB (...) STORE AS SECUREFILE ASSM表領域が必要

LOBセグメントの管理は、以下の手順フローで対処するのが効率的です。

  1. 現状確認: DBA_SEGMENTS + DBA_LOBS でLOBサイズとテーブルの紐付きを確認
  2. 不要データの削除: バッチ処理で安全にDELETE(大量データの場合)
  3. 領域の解放: SecureFileなら SHRINK SPACE、BasicFileなら MOVE LOB で再編成
  4. インデックス再構築: MOVEを使った場合は必ず REBUILD を実行
  5. 表領域の縮小: DBA_FREE_SPACE で空きを確認し、DATAFILE RESIZE で縮小
  6. 予防策: LOB専用表領域の分離、SecureFileの採用、サイズ監視の定期実行

特に「DELETEしても領域が返却されない」という点は最大の落とし穴です。LOBセグメントの領域を実際に解放するには、MOVE LOBまたはSHRINK SPACEが必須であることを覚えておいてください。BasicFile LOBを使っている場合は、この機会にSecureFileへの変換も検討しましょう。

This website stores cookies on your computer. These cookies are used to provide a more personalized experience and to track your whereabouts around our website in compliance with the European General Data Protection Regulation. If you decide to to opt-out of any future tracking, a cookie will be setup in your browser to remember this choice for one year.

Accept or Deny