【Oracle】容量予約の仕組みを完全解説|PCTFREE・PCTUSED・STORAGE句・ASSM・エクステント管理

【Oracle】容量予約の仕組みを完全解説|PCTFREE・PCTUSED・STORAGE句・ASSM・エクステント管理 Oracle

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 が多いテーブル(可変長列が増える)に適用。ブロック効率は下がるが行移行を防ぐ
PCTFREE が小さすぎると「行移行(Row Migration)」が発生する
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;
ASSM 環境では PCTUSED は無視される
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 を指定(肥大化防止)
ローカル管理表領域では NEXT・PCTINCREASE は無視される
Oracle 9i 以降のデフォルトである ローカル管理表領域(Locally Managed Tablespace) では、エクステントのサイズは表領域側の設定(AUTOALLOCATE または UNIFORM SIZE)で決まります。STORAGE 句の NEXTPCTINCREASE は無視されます。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)

Q PCTFREE を下げればテーブルのサイズが小さくなる?
A

PCTFREE を変更しても 既存ブロックの予約済み領域はすぐには変わりませんALTER TABLE ... PCTFREE n を実行すると新規ブロックへの INSERT から適用されます。既存ブロックを変更するには ALTER TABLE ... MOVE で再配置する必要があります。

なお PCTFREE を下げてもセグメントサイズ(HWM)は縮まりません。サイズ削減には MOVE または SHRINK SPACE が必要です。

Q 行移行(Row Migration)が起きているか確認するには?
A

統計収集後に 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 で再配置することを検討してください。

Q ローカル管理表領域で NEXT を指定しても意味がない?
A

はい、ローカル管理表領域(Oracle 9i 以降のデフォルト)では STORAGE 句の NEXT と PCTINCREASE は無視されます。エクステントサイズは表領域の設定(AUTOALLOCATE か UNIFORM SIZE)に従います。

INITIAL だけは有効です。CREATE TABLE t STORAGE (INITIAL 64M) とすれば最初のエクステント合計サイズを指定できます。古い書籍や運用スクリプトに NEXT が書いてあっても害はありませんが、実際には機能しません。

Q ASSM と MSSM はどちらが良い?
A

ほとんどの場合 ASSM(AUTO)を推奨します。並列 INSERT 時のフリーリスト競合が発生せず、現代の OLTP ・バッチ処理の両方で安定して動作します。

MSSM(MANUAL)を使う理由があるのは、非常に古いアプリケーションとの互換性が必要な場合や、DBA が細かく空きブロック管理をチューニングしたい特殊なケースに限られます。新規テーブルスペースは常に ASSM で作成してください。

Q エクステント数が増えすぎるとパフォーマンスに影響する?
A

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 設定を確認することを習慣にしてください。