Oracle のパフォーマンスチューニングにおいて、テーブルとインデックスを異なる表領域に分離する設計は広く採用されています。I/O の分散、バックアップ戦略の柔軟化、管理性の向上など複数のメリットがあります。
本記事では、INDEX 専用表領域の作成手順、CREATE INDEX 時の TABLESPACE 指定、既存インデックスの移動、サイジングの目安、命名規則まで解説します。
この記事でわかること
・INDEX 専用表領域を設けるメリットとデメリット
・INDEX 専用表領域の作成手順
・CREATE INDEX 時に TABLESPACE を指定する方法
・既存インデックスを別の表領域に移動する方法(REBUILD TABLESPACE)
・表領域の命名規則と設計パターン
・サイジングの目安
・ASM 環境での考慮点
・INDEX 専用表領域を設けるメリットとデメリット
・INDEX 専用表領域の作成手順
・CREATE INDEX 時に TABLESPACE を指定する方法
・既存インデックスを別の表領域に移動する方法(REBUILD TABLESPACE)
・表領域の命名規則と設計パターン
・サイジングの目安
・ASM 環境での考慮点
INDEX 専用表領域を設けるメリット
| メリット | 説明 |
|---|---|
| I/O の分散 | テーブルとインデックスを異なるディスクに配置すれば、同時読み書きで I/O が分散される |
| 管理性の向上 | インデックスだけ REBUILD / COALESCE / バックアップする際にスコープが明確 |
| バックアップ戦略の柔軟化 | インデックス表領域は再構築可能なためバックアップから除外できる(Data Pump EXCLUDE=INDEX) |
| 断片化対策の分離 | インデックスはテーブルより断片化しやすい。分離すれば断片化の影響が表領域をまたがない |
| 容量管理の明確化 | テーブルとインデックスの容量を個別に把握・監視できる |
デメリットと注意点
| デメリット | 対策 |
|---|---|
| 管理する表領域の数が増える | 命名規則を統一して管理負荷を軽減 |
| ASM 環境では I/O 分散のメリットが薄い | ASM が自動的にディスク間で分散するため、I/O 分散目的なら不要な場合もある |
| CREATE INDEX 時に TABLESPACE 指定を忘れるリスク | デフォルト INDEX 表領域の設定やコードレビューで防止 |
ASM 環境でも管理性のメリットは残る
ASM ではディスク I/O は自動分散されるため、パフォーマンス面のメリットは薄くなります。しかし「インデックスだけ再構築」「インデックス表領域のサイズ監視」など管理面のメリットはASM 環境でも有効です。
ASM ではディスク I/O は自動分散されるため、パフォーマンス面のメリットは薄くなります。しかし「インデックスだけ再構築」「インデックス表領域のサイズ監視」など管理面のメリットはASM 環境でも有効です。
INDEX 専用表領域の作成
SQL(INDEX 専用表領域の作成)
-- テーブル用表領域(データ格納用)
CREATE TABLESPACE app_data
DATAFILE '/oracle/disk1/app_data01.dbf'
SIZE 5G AUTOEXTEND ON NEXT 500M MAXSIZE 20G;
-- INDEX 専用表領域(別ディスクに配置が理想)
CREATE TABLESPACE app_index
DATAFILE '/oracle/disk2/app_index01.dbf'
SIZE 2G AUTOEXTEND ON NEXT 200M MAXSIZE 10G;
-- ユーザーに QUOTA を付与
ALTER USER app_user QUOTA UNLIMITED ON app_data;
ALTER USER app_user QUOTA UNLIMITED ON app_index;
異なる物理ディスクに配置する
I/O 分散の効果を最大化するには、テーブル用表領域とインデックス用表領域を異なる物理ディスク(または異なる RAID グループ)に配置します。同じディスク上の別ディレクトリに分けても I/O 分散の効果はありません。
I/O 分散の効果を最大化するには、テーブル用表領域とインデックス用表領域を異なる物理ディスク(または異なる RAID グループ)に配置します。同じディスク上の別ディレクトリに分けても I/O 分散の効果はありません。
表領域の命名規則
| パターン | テーブル用 | インデックス用 | 例 |
|---|---|---|---|
| アプリ名_DATA / _IDX | APP_DATA | APP_IDX | HR_DATA / HR_IDX |
| スキーマ名_TBL / _IND | HR_TBL | HR_IND | SALES_TBL / SALES_IND |
| 用途_TS | APP_TABLE_TS | APP_INDEX_TS | CRM_TABLE_TS / CRM_INDEX_TS |
命名規則を統一することで運用ミスを防止
「_DATA = テーブル」「_IDX = インデックス」のルールを統一すれば、
「_DATA = テーブル」「_IDX = インデックス」のルールを統一すれば、
ALTER TABLESPACE app_idx COALESCE のような操作で対象を間違えるリスクが減ります。CREATE INDEX 時に TABLESPACE を指定する
SQL(TABLESPACE 指定付き CREATE INDEX)
-- インデックスを INDEX 専用表領域に作成
CREATE INDEX idx_emp_dept ON employees(department_id)
TABLESPACE app_index;
-- PRIMARY KEY 制約のインデックスも表領域を指定可能
ALTER TABLE employees ADD CONSTRAINT pk_emp
PRIMARY KEY (employee_id)
USING INDEX TABLESPACE app_index;
-- CREATE TABLE 内で PK の表領域を指定
CREATE TABLE orders (
order_id NUMBER,
order_date DATE,
amount NUMBER,
CONSTRAINT pk_orders PRIMARY KEY (order_id)
USING INDEX TABLESPACE app_index
) TABLESPACE app_data;
TABLESPACE を指定しないとどうなるか
TABLESPACE 句を省略すると、ユーザーのDEFAULT TABLESPACEにインデックスが作成されます。テーブルと同じ表領域になるため、分離の意味がなくなります。
SQL(デフォルト表領域の確認)
-- ユーザーのデフォルト表領域を確認 SELECT username, default_tablespace FROM dba_users WHERE username = 'APP_USER'; -- デフォルト表領域はテーブル用にして、 -- インデックスは CREATE INDEX 時に毎回 TABLESPACE を指定する運用が一般的
既存インデックスを別の表領域に移動する
SQL(REBUILD TABLESPACE で移動)
-- 既存インデックスを INDEX 専用表領域に移動 ALTER INDEX idx_emp_dept REBUILD TABLESPACE app_index; -- ONLINE で移動(DML をブロックしない) ALTER INDEX idx_emp_dept REBUILD TABLESPACE app_index ONLINE; -- NOLOGGING + PARALLEL で高速移動(メンテナンス時間帯) ALTER INDEX idx_emp_dept REBUILD TABLESPACE app_index NOLOGGING PARALLEL 4; ALTER INDEX idx_emp_dept LOGGING; ALTER INDEX idx_emp_dept NOPARALLEL;
SQL(スキーマの全インデックスを一括移動)
-- テーブル用表領域にあるインデックスを INDEX 表領域に一括移動
BEGIN
FOR rec IN (
SELECT index_name FROM user_indexes
WHERE tablespace_name = 'APP_DATA' -- テーブル用表領域にある
AND index_type IN ('NORMAL', 'BITMAP', 'FUNCTION-BASED NORMAL')
) LOOP
EXECUTE IMMEDIATE 'ALTER INDEX ' || rec.index_name
|| ' REBUILD TABLESPACE APP_INDEX ONLINE';
END LOOP;
END;
/
REBUILD ONLINE は Enterprise Edition 専用
Standard Edition では
Standard Edition では
REBUILD ONLINE は使えません。通常の REBUILD TABLESPACE はテーブルロックが発生するため、業務時間外に実行してください。インデックスの表領域配置を確認する
SQL(インデックスがどの表領域にあるか確認)
-- インデックスごとの表領域を確認
SELECT index_name, table_name, tablespace_name, index_type
FROM user_indexes
ORDER BY tablespace_name, table_name;
-- テーブルとインデックスの表領域を対比
SELECT t.table_name,
t.tablespace_name AS table_ts,
i.index_name,
i.tablespace_name AS index_ts,
CASE WHEN t.tablespace_name = i.tablespace_name THEN '*** SAME ***' END AS warning
FROM user_tables t
JOIN user_indexes i ON t.table_name = i.table_name
ORDER BY t.table_name, i.index_name;
-- warning = '*** SAME ***' のインデックスは分離されていない
SQL(INDEX 表領域のサイズを確認)
-- 表領域ごとの合計サイズ(テーブル vs インデックスの比較)
SELECT tablespace_name, segment_type,
COUNT(*) AS segment_count,
ROUND(SUM(bytes)/1024/1024, 1) AS total_mb
FROM user_segments
WHERE segment_type IN ('TABLE', 'INDEX')
GROUP BY tablespace_name, segment_type
ORDER BY tablespace_name, segment_type;
INDEX 表領域のサイジング
| 目安 | 説明 |
|---|---|
| テーブル表領域の 30〜50% | 一般的なインデックス量。テーブルサイズの 30〜50% 程度が目安 |
| インデックス数が多い場合: 50〜100% | テーブルあたり 5 個以上のインデックスがある場合 |
| B-Tree インデックス | 元データの 10〜30% 程度のサイズ |
| ビットマップインデックス | 元データの 5〜10% 程度(カーディナリティが低い列) |
SQL(現在のインデックス合計サイズを確認)
-- スキーマ全体のインデックスサイズ
SELECT ROUND(SUM(bytes)/1024/1024/1024, 2) AS total_index_gb
FROM user_segments
WHERE segment_type = 'INDEX';
-- テーブル別のインデックスサイズ
SELECT i.table_name,
COUNT(*) AS idx_count,
ROUND(SUM(s.bytes)/1024/1024, 1) AS idx_total_mb
FROM user_indexes i
JOIN user_segments s ON i.index_name = s.segment_name AND s.segment_type = 'INDEX'
GROUP BY i.table_name
ORDER BY idx_total_mb DESC;
AUTOEXTEND ON + MAXSIZE を必ず設定
INDEX 表領域にも AUTOEXTEND ON を設定してください。インデックスの REBUILD でサイズが一時的に 2 倍近くになることがあるため、MAXSIZE はテーブル表領域より余裕を持って設定します。
INDEX 表領域にも AUTOEXTEND ON を設定してください。インデックスの REBUILD でサイズが一時的に 2 倍近くになることがあるため、MAXSIZE はテーブル表領域より余裕を持って設定します。
表領域設計のパターン
パターン(1): シンプル分離(小〜中規模)
| 表領域 | 用途 | 配置先 |
|---|---|---|
| APP_DATA | テーブル | ディスク 1 |
| APP_INDEX | インデックス | ディスク 2 |
| TEMP | 一時領域 | ディスク 3 |
| UNDO | UNDO | ディスク 1 |
パターン(2): アプリケーション別分離(大規模)
| 表領域 | 用途 |
|---|---|
| HR_DATA / HR_IDX | HR アプリのテーブル / インデックス |
| SALES_DATA / SALES_IDX | 販売管理のテーブル / インデックス |
| AUDIT_DATA | 監査ログ(インデックスは少ないため分離不要) |
| LOB_DATA | LOB セグメント専用 |
パターン(3): 読み書き特性で分離
| 表領域 | 特性 |
|---|---|
| MASTER_DATA / MASTER_IDX | マスタ(参照多 / 更新少): READ ONLY に設定可能 |
| TXN_DATA / TXN_IDX | トランザクション(更新多): 高速ディスクに配置 |
| ARCHIVE_DATA | 過去データ(参照のみ): 低速ディスクでもOK |
Data Pump での INDEX 表領域の扱い
Shell(エクスポート/インポート時の表領域制御)
# インデックスを除外してインポート → 後から INDEX 表領域に再作成
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_schema.dmp \
schemas=HR \
exclude=INDEX
# 別環境で表領域名が異なる場合は REMAP_TABLESPACE
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_schema.dmp \
schemas=HR \
remap_tablespace=PROD_IDX:DEV_IDX
# SEGMENT_ATTRIBUTES:N で元の表領域情報を無視
impdp hr/password \
directory=DP_DIR \
dumpfile=hr_schema.dmp \
schemas=HR \
transform=SEGMENT_ATTRIBUTES:N
実務パターン集
パターン(1): 新規プロジェクトの表領域構築
SQL
-- テーブル用
CREATE TABLESPACE crm_data
DATAFILE '/oracle/disk1/crm_data01.dbf'
SIZE 5G AUTOEXTEND ON NEXT 500M MAXSIZE 30G;
-- インデックス用
CREATE TABLESPACE crm_index
DATAFILE '/oracle/disk2/crm_index01.dbf'
SIZE 2G AUTOEXTEND ON NEXT 200M MAXSIZE 15G;
-- ユーザーに QUOTA
ALTER USER crm_user QUOTA UNLIMITED ON crm_data;
ALTER USER crm_user QUOTA UNLIMITED ON crm_index;
-- テーブル作成(PK インデックスも INDEX 表領域に)
CREATE TABLE crm_user.customers (
customer_id NUMBER,
name VARCHAR2(200),
email VARCHAR2(200),
CONSTRAINT pk_customers PRIMARY KEY (customer_id)
USING INDEX TABLESPACE crm_index
) TABLESPACE crm_data;
CREATE INDEX crm_user.idx_cust_email ON crm_user.customers(email)
TABLESPACE crm_index;
パターン(2): 既存環境のインデックスを分離
SQL
-- (1) INDEX 表領域を作成
CREATE TABLESPACE app_index
DATAFILE '/oracle/disk2/app_index01.dbf'
SIZE 2G AUTOEXTEND ON NEXT 200M MAXSIZE 10G;
-- (2) QUOTA 付与
ALTER USER app_user QUOTA UNLIMITED ON app_index;
-- (3) 全インデックスを一括移動
BEGIN
FOR rec IN (
SELECT index_name FROM user_indexes
WHERE tablespace_name = 'APP_DATA'
) LOOP
EXECUTE IMMEDIATE 'ALTER INDEX ' || rec.index_name
|| ' REBUILD TABLESPACE APP_INDEX ONLINE';
END LOOP;
END;
/
パターン(3): 分離状態の定期チェックスクリプト
SQL(テーブルと同じ表領域にあるインデックスを検出)
-- テーブルと同じ表領域に配置されたインデックス = 分離できていない
SELECT t.table_name, i.index_name,
t.tablespace_name AS table_ts,
i.tablespace_name AS index_ts
FROM user_tables t
JOIN user_indexes i ON t.table_name = i.table_name
WHERE t.tablespace_name = i.tablespace_name
ORDER BY t.table_name;
-- 結果があれば REBUILD TABLESPACE で移動を検討
よくある質問
QINDEX 専用表領域は必須ですか?
A必須ではありません。小規模なデータベースや ASM 環境では分離のメリットが小さい場合もあります。ただし中〜大規模環境や「インデックスだけ管理したい」ニーズがある場合は分離を推奨します。
QASM 環境でもインデックス表領域を分ける意味はありますか?
AI/O 分散のメリットは ASM が自動で行うため薄くなりますが、管理面のメリット(サイズ監視の分離・バックアップ戦略・REBUILD 対象の明確化)は残ります。
QCREATE INDEX で TABLESPACE を指定し忘れるとどうなりますか?
Aユーザーの DEFAULT TABLESPACE(通常はテーブル用表領域)にインデックスが作成されます。分離の意味がなくなるため、開発チームの SQL テンプレートに TABLESPACE 指定を含めるか、後から REBUILD TABLESPACE で移動してください。
QPRIMARY KEY のインデックスも INDEX 表領域に作れますか?
Aはい。
CONSTRAINT pk PRIMARY KEY (col) USING INDEX TABLESPACE app_index で指定できます。CREATE TABLE 文内でも ALTER TABLE ADD CONSTRAINT でも指定可能です。QREBUILD TABLESPACE で移動中にテーブルにアクセスできますか?
A
REBUILD ONLINE(Enterprise Edition)なら DML を止めずに移動できます。通常の REBUILD はテーブルロックが発生するため業務時間外に実施してください。Standard Edition では ONLINE は使えません。QINDEX 表領域のサイズはテーブル表領域の何割くらい必要ですか?
A一般的にはテーブル表領域の 30〜50% が目安です。テーブルあたりのインデックス数が多い場合(5 個以上)は 50〜100% になることもあります。現在のインデックスの合計サイズを
user_segments WHERE segment_type='INDEX' で確認してください。まとめ
INDEX 専用表領域の設計の要点をまとめます。
| やりたいこと | SQL |
|---|---|
| INDEX 専用表領域を作成 | CREATE TABLESPACE app_index DATAFILE ‘…’ SIZE 2G AUTOEXTEND ON |
| CREATE INDEX 時に表領域を指定 | CREATE INDEX idx ON table(col) TABLESPACE app_index |
| PK インデックスの表領域を指定 | CONSTRAINT pk PRIMARY KEY (col) USING INDEX TABLESPACE app_index |
| 既存インデックスを移動 | ALTER INDEX idx REBUILD TABLESPACE app_index [ONLINE] |
| 全インデックスを一括移動 | PL/SQL で user_indexes をループして REBUILD TABLESPACE |
| 分離状態を確認 | user_tables と user_indexes を JOIN して tablespace_name を比較 |
| インデックスのサイズ確認 | SELECT SUM(bytes) FROM user_segments WHERE segment_type=’INDEX’ |
表領域の管理全般は「表領域(Tablespace)完全ガイド」、インデックスの作成・管理は「インデックスの作成・再構築・削除完全ガイド」、データファイルの追加は「データファイルを追加・変更する方法」も併せて参照してください。

