Oracle が「なぜ DELETE してもサイズが減らないのか」「なぜテーブルの各ブロックに余白があるのか」——その答えは Oracle の 容量予約の仕組み にあります。本記事では、ブロックレベルの予約(PCTFREE/PCTUSED)からセグメントレベルの事前割り当て(STORAGE 句)、表領域レベルの管理方式(ASSM/AUTOALLOCATE/UNIFORM)まで、Oracle の領域管理を体系的に解説します。
- PCTFREE / PCTUSED の意味と行移行(Row Migration)が起きる理由
- STORAGE 句の INITIAL / NEXT / MINEXTENTS / MAXEXTENTS / PCTINCREASE の役割
- 自動セグメント領域管理(ASSM)と手動管理(MSSM)の違いと確認方法
- 表領域の AUTOALLOCATE と UNIFORM SIZE の使い分け
- 現在の容量予約設定を確認する SQL
- パフォーマンスに影響する容量予約の注意点
Oracle の容量予約とは
Oracle はデータを データファイル → 表領域 → セグメント → エクステント → ブロック という階層で管理します。容量予約はこの各階層で発生します。
| 階層 | 予約の仕組み | 設定パラメータ |
|---|---|---|
| ブロック内 | UPDATE 用の空き領域をブロックごとに予約 | PCTFREE / PCTUSED |
| セグメント | テーブル/インデックス作成時の初期割り当てと拡張単位 | STORAGE 句(INITIAL/NEXT 等) |
| 表領域 | エクステントの割り当て方式と空き領域の管理方法 | AUTOALLOCATE / UNIFORM / ASSM |
ブロックレベルの予約:PCTFREE と PCTUSED
PCTFREE — UPDATE のための空き領域を予約する
PCTFREE(Percent Free)は、各データブロックの中で UPDATE 処理のために確保しておく空き領域の割合です。デフォルトは 10(10%)です。
| 設定 | 動作 |
|---|---|
| PCTFREE = 10(デフォルト) | ブロックが 90% 埋まったら新規 INSERT を止め、10% を UPDATE 用に予約 |
| PCTFREE = 0 | ブロックを 100% まで INSERT に使う。UPDATE が多いと行移行が発生しやすい |
| PCTFREE = 30 | UPDATE が多いテーブル(可変長列が増える)に適用。ブロック効率は下がるが行移行を防ぐ |
UPDATE で行が拡張しブロックに収まらなくなると、Oracle は行を別ブロックに移動します(行移行)。元のブロックにはポインタが残るため、1 回の SELECT で複数ブロックを読む必要が生じ、パフォーマンスが低下します。
UPDATE が多いテーブルや VARCHAR2 列に後から値が追加されるテーブルは PCTFREE を大きめに設定してください。
PCTUSED — フリーリストへの返却タイミングを制御する(MSSM 環境のみ)
PCTUSED(Percent Used)は、ブロックの使用率が PCTUSED を下回ったとき、そのブロックを再び INSERT 可能な「フリーリスト」に戻す閾値です。デフォルトは 40 です。
-- PCTFREE=10 / PCTUSED=40 の動作イメージ -- 1. ブロックが 90% まで埋まる → INSERT 停止(PCTFREE 10% を確保) -- 2. DELETE が進み使用率が 40% を下回る → フリーリストに返却 -- 3. 再び INSERT 可能になる -- 設定例:UPDATE が多いテーブル CREATE TABLE orders ( order_id NUMBER, memo VARCHAR2(4000) ) PCTFREE 30 PCTUSED 50;
Oracle 9i 以降のデフォルトである ASSM(Automatic Segment Space Management) では、空きブロックの管理にビットマップを使うため PCTUSED は意味を持ちません。ASSM 環境では PCTFREE のみが有効です。
セグメントレベルの予約:STORAGE 句
テーブルやインデックスを作成する際に STORAGE 句で 初期割り当て量と拡張のルール を指定できます。
CREATE TABLE large_logs ( log_id NUMBER, log_text VARCHAR2(4000) ) STORAGE ( INITIAL 64M -- 最初に確保するエクステントの合計サイズ NEXT 16M -- 2 回目以降の拡張サイズ(ディクショナリ管理表領域のみ有効) MINEXTENTS 1 -- 最低限確保するエクステント数 MAXEXTENTS UNLIMITED -- 最大エクステント数(UNLIMITED = 無制限) PCTINCREASE 0 -- 拡張サイズの増加率(ローカル管理表領域では無視) );
STORAGE 句の各パラメータ詳細
| パラメータ | 意味 | 推奨値 |
|---|---|---|
INITIAL |
セグメント作成時に確保する初期エクステントの合計サイズ | データ量を見越して大きめに設定 |
NEXT |
2 回目以降のエクステント拡張サイズ(ディクショナリ管理表領域のみ) | INITIAL と同程度 |
MINEXTENTS |
作成時に確保する最低エクステント数 | 通常は 1 |
MAXEXTENTS |
エクステントの最大数 | UNLIMITED 推奨(Oracle 8i 以降) |
PCTINCREASE |
NEXT を毎回何 % 増やすか(ローカル管理表領域では無視) | 0 を指定(肥大化防止) |
Oracle 9i 以降のデフォルトである ローカル管理表領域(Locally Managed Tablespace) では、エクステントのサイズは表領域側の設定(AUTOALLOCATE または UNIFORM SIZE)で決まります。STORAGE 句の
NEXT と PCTINCREASE は無視されます。INITIAL は有効です。
表領域レベルの管理方式
エクステント管理:AUTOALLOCATE vs UNIFORM SIZE
| 方式 | 動作 | 向いている用途 |
|---|---|---|
EXTENT MANAGEMENT LOCAL AUTOALLOCATE(デフォルト) |
Oracle がエクステントサイズを自動決定(小さいセグメントは小さく、大きくなるにつれ拡大) | 汎用。サイズが読めないオブジェクト混在環境 |
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M |
すべてのエクステントを指定サイズで統一 | エクステント断片化を防ぎたい場合・大量の同サイズオブジェクト |
-- AUTOALLOCATE でのエクステントサイズ実績を確認する SELECT segment_name, extent_id, bytes / 1024 AS extent_kb FROM user_extents WHERE segment_name = 'LARGE_LOGS' ORDER BY extent_id; -- 例: 初期は 64KB → データが増えると 1MB → 8MB → 64MB へと拡大
-- UNIFORM SIZE 1MB の表領域を作成(エクステントを均一化) CREATE TABLESPACE uniform_ts DATAFILE '/oradata/ORCL/uniform01.dbf' SIZE 1024M EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M SEGMENT SPACE MANAGEMENT AUTO; -- AUTOALLOCATE(デフォルト)の表領域を作成 CREATE TABLESPACE auto_ts DATAFILE '/oradata/ORCL/auto01.dbf' SIZE 1024M EXTENT MANAGEMENT LOCAL AUTOALLOCATE SEGMENT SPACE MANAGEMENT AUTO;
セグメント領域管理:ASSM vs MSSM
| 方式 | 設定 | 特徴 |
|---|---|---|
| ASSM(Automatic Segment Space Management) | SEGMENT SPACE MANAGEMENT AUTO(デフォルト) |
ビットマップで空きブロックを管理。並列 INSERT に強い。PCTUSED 不要 |
| MSSM(Manual Segment Space Management) | SEGMENT SPACE MANAGEMENT MANUAL |
フリーリストで管理。古い方式。単一 INSERT に効率的な場合もある |
現在の容量予約設定を確認する SQL
テーブルの PCTFREE・PCTUSED・STORAGE 設定を確認
-- テーブルの容量予約設定を一覧表示 SELECT table_name, pct_free, pct_used, ini_trans, max_trans, initial_extent / 1024 / 1024 AS initial_mb, next_extent / 1024 / 1024 AS next_mb, min_extents, max_extents, pct_increase, compression, row_movement FROM user_tables ORDER BY table_name; -- 特定テーブルの詳細 SELECT * FROM user_tables WHERE table_name = 'EMPLOYEES';
表領域の管理方式を確認
-- 表領域のエクステント管理方式・セグメント管理方式を確認 SELECT tablespace_name, extent_management, -- LOCAL / DICTIONARY allocation_type, -- SYSTEM(AUTOALLOCATE)/ UNIFORM segment_space_management, -- AUTO(ASSM)/ MANUAL(MSSM) initial_extent / 1024 AS initial_kb, next_extent / 1024 AS next_kb, min_extents, max_extents FROM dba_tablespaces ORDER BY tablespace_name;
セグメントのエクステント数・サイズを確認
-- テーブルのエクステント一覧(エクステント数が多い場合は断片化のサイン) SELECT segment_name, extent_id, ROUND(bytes / 1024 / 1024, 2) AS extent_mb, blocks FROM user_extents WHERE segment_name = 'EMPLOYEES' AND segment_type = 'TABLE' ORDER BY extent_id; -- エクステント数が多いセグメントを抽出 SELECT segment_name, segment_type, COUNT(*) AS extent_count, ROUND(SUM(bytes) / 1024 / 1024, 2) AS total_mb FROM user_extents GROUP BY segment_name, segment_type HAVING COUNT(*) > 100 ORDER BY extent_count DESC;
容量予約の設定変更
既存テーブルの PCTFREE を変更する
-- PCTFREE を変更(次回 INSERT から適用。既存ブロックは変わらない) ALTER TABLE employees PCTFREE 20; -- PCTFREE と STORAGE を同時に変更 ALTER TABLE large_logs PCTFREE 15 STORAGE (NEXT 32M MAXEXTENTS UNLIMITED);
エクステント断片化を解消する(MOVE)
-- テーブルを再配置してエクステントを統合(オフライン・インデックス再構築必要) ALTER TABLE large_logs MOVE STORAGE (INITIAL 128M); -- 移動後はインデックスが UNUSABLE になるため再構築 ALTER INDEX idx_large_logs_id REBUILD;
実務での推奨設定
| テーブルの性質 | PCTFREE の目安 | 理由 |
|---|---|---|
| INSERT のみ(ログ、履歴テーブル) | 1〜5 | UPDATE がないため予約不要 |
| 通常の OLTP テーブル | 10(デフォルト) | バランスが良い |
| UPDATE が多い(可変長列あり) | 20〜30 | 行移行を防ぐ |
| インデックス(通常) | 10(デフォルト) | 標準的な設定 |
| インデックス(単調増加キー) | 0〜5 | 末尾ブロックへの INSERT のみで UPDATE なし |
よくある質問(FAQ)
PCTFREE を変更しても 既存ブロックの予約済み領域はすぐには変わりません。ALTER TABLE ... PCTFREE n を実行すると新規ブロックへの INSERT から適用されます。既存ブロックを変更するには ALTER TABLE ... MOVE で再配置する必要があります。
なお PCTFREE を下げてもセグメントサイズ(HWM)は縮まりません。サイズ削減には MOVE または SHRINK SPACE が必要です。
統計収集後に dba_tables.chain_cnt で確認できます。
-- 統計収集
EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES', CASCADE => TRUE);
-- 行移行・行連鎖の件数を確認
SELECT table_name, num_rows, chain_cnt,
ROUND(chain_cnt / NULLIF(num_rows, 0) * 100, 2) AS chain_pct
FROM dba_tables
WHERE owner = 'HR'
AND chain_cnt > 0
ORDER BY chain_pct DESC;
chain_pct が数 % を超えるようなら PCTFREE を上げて MOVE で再配置することを検討してください。
はい、ローカル管理表領域(Oracle 9i 以降のデフォルト)では STORAGE 句の NEXT と PCTINCREASE は無視されます。エクステントサイズは表領域の設定(AUTOALLOCATE か UNIFORM SIZE)に従います。
INITIAL だけは有効です。CREATE TABLE t STORAGE (INITIAL 64M) とすれば最初のエクステント合計サイズを指定できます。古い書籍や運用スクリプトに NEXT が書いてあっても害はありませんが、実際には機能しません。
ほとんどの場合 ASSM(AUTO)を推奨します。並列 INSERT 時のフリーリスト競合が発生せず、現代の OLTP ・バッチ処理の両方で安定して動作します。
MSSM(MANUAL)を使う理由があるのは、非常に古いアプリケーションとの互換性が必要な場合や、DBA が細かく空きブロック管理をチューニングしたい特殊なケースに限られます。新規テーブルスペースは常に ASSM で作成してください。
Oracle のフルスキャンはエクステントをまたいでも効率的に処理されるため、エクステント数自体がパフォーマンスに直接影響することはほぼありません。ただし数千以上のエクステントを持つセグメントは管理上の問題(スペースマップ肥大化)になることがあります。
エクステント数を減らすには ALTER TABLE ... MOVE STORAGE (INITIAL 大きいサイズ) で再配置するか、UNIFORM SIZE を大きくした表領域に移動してください。
まとめ
| 設定 | 影響範囲 | 主な用途 |
|---|---|---|
PCTFREE |
各データブロック | UPDATE 用の空き領域予約。行移行防止 |
PCTUSED |
各データブロック(MSSM のみ) | フリーリスト返却タイミング制御 |
INITIAL(STORAGE 句) |
セグメント全体 | 作成時の初期割り当てサイズ |
NEXT(STORAGE 句) |
セグメント全体(ローカル管理表領域では無視) | 拡張単位サイズ。ローカル管理では AUTOALLOCATE/UNIFORM が優先 |
AUTOALLOCATE |
表領域 | Oracle がエクステントサイズを自動決定 |
UNIFORM SIZE |
表領域 | エクステントを均一サイズで統一 |
ASSM(AUTO) |
表領域 | ビットマップで空きブロックを自動管理 |
Oracle の容量予約は ブロック・セグメント・表領域の 3 層 で管理されています。現代の Oracle(9i 以降)ではほとんどの管理が自動化されていますが、UPDATE が多いテーブルの PCTFREE 調整と、表領域作成時の ASSM/UNIFORM SIZE 選択は今でも重要な設定です。パフォーマンス問題が発生したら行移行の有無と PCTFREE 設定を確認することを習慣にしてください。

