Oracle のインデックスは、大量データを扱うシステムでは相当のストレージを消費します。複合インデックス(複数列の組み合わせ)では、先行列に繰り返し値が多い場合、インデックスキー圧縮(Key Compression)によってストレージを大幅に削減できます。
ストレージ削減の効果は単なるコスト削減にとどまりません。インデックスが小さくなるほどバッファキャッシュに収まりやすくなり、I/O が減少してクエリのパフォーマンスが向上します。Oracle 12c からは Advanced Index Compression が加わり、一意インデックスを含むすべての B-Tree インデックスに圧縮を適用できるようになりました。
- インデックスキー圧縮の仕組みと COMPRESS n の意味
- 複合インデックスで圧縮が効果的なケースと効果がないケース
- CREATE INDEX … COMPRESS n でインデックスを作成する方法
- Oracle 12c 以降の Advanced Index Compression(COMPRESS ADVANCED LOW/HIGH)
- 既存インデックスを ALTER INDEX REBUILD COMPRESS で再構築する方法
- DBA_INDEXES でインデックス圧縮状態を確認する方法
- ANALYZE INDEX VALIDATE STRUCTURE で圧縮効果を測定する方法
インデックスキー圧縮の仕組み
B-Tree インデックスの各リーフブロックには、インデックスエントリが格納されています。通常の複合インデックスでは、エントリごとにすべてのキー列の値が格納されます。例えば (department_id, job_id, employee_id) の複合インデックスでdepartment_id=50 の行が 100 件あれば、”50″ という値が 100 回格納されます。
キー圧縮では、同一ブロック内で繰り返す先行列の値を1回だけ格納し、後続のエントリは省略形で参照します。先行列の値が多く繰り返す(カーディナリティが低い)ほど圧縮効果が高くなります。
| 項目 | 通常インデックス | COMPRESS 1(先頭1列を圧縮) |
|---|---|---|
| 格納形式 | 各エントリにすべての列値を格納 | ブロック内の先行列値を圧縮ヘッダーに1回格納 |
| 効果的な条件 | すべてのケース | 先行列に繰り返し値が多い複合インデックス |
| 一意インデックス | 通常通り | 標準キー圧縮は最後の列を除いた列に適用 |
| INSERT/UPDATE オーバーヘッド | なし | ごくわずか(圧縮処理) |
| Oracle バージョン | 全バージョン | 全バージョン(B-Tree インデックス) |
COMPRESS n でインデックスを作成する
-- COMPRESS n: 先頭 n 列をキー圧縮する(n はインデックスの列数 -1 以下)
-- 例: (department_id, job_id, employee_id) の複合インデックスで先頭 2 列を圧縮する
CREATE INDEX emp_dept_job_idx
ON employees(department_id, job_id, employee_id)
COMPRESS 2; -- department_id と job_id の繰り返しを圧縮する
-- COMPRESS だけ(n なし)= COMPRESS 1 と同義(先頭1列を圧縮)
-- 圧縮なしの場合と比較するためにもう一方のインデックスも作成する
CREATE INDEX emp_dept_job_nocomp_idx
ON employees(department_id, job_id, employee_id)
NOCOMPRESS; -- 明示的に圧縮なし(デフォルト)
-- インデックス情報を確認する
SELECT
index_name,
compression, -- ENABLED / DISABLED
leaf_blocks, -- リーフブロック数(少ないほどコンパクト)
num_rows,
blevel -- B-Tree の深さ
FROM DBA_INDEXES
WHERE table_name = 'EMPLOYEES'
AND index_name IN ('EMP_DEPT_JOB_IDX', 'EMP_DEPT_JOB_NOCOMP_IDX')
ORDER BY index_name;
-- 統計更新後に比較する
EXEC DBMS_STATS.GATHER_INDEX_STATS('HR', 'EMP_DEPT_JOB_IDX');
EXEC DBMS_STATS.GATHER_INDEX_STATS('HR', 'EMP_DEPT_JOB_NOCOMP_IDX');
圧縮効果は「先行列の値の繰り返し度合い」に依存します。
(department_id, job_id, employee_id) の例で、department_id が 10 種類・job_id が 20 種類・employee_id がユニークな場合、COMPRESS 2 にすることで department_id + job_id の組み合わせ繰り返しを圧縮できます。ただし n をインデックスの全列数に設定すると、ROWID まで圧縮対象に含まれる場合がありエラーになることがあります。非一意インデックスでは「全列数 – 1」が上限です。
Advanced Index Compression(Oracle 12c 以降)
Oracle 12c Enterprise Edition で導入された Advanced Index Compression は、標準キー圧縮では対応できなかったケース(一意インデックス・単一列インデックス)にも圧縮を適用できます。Oracle が自動的に最適な圧縮方式を選択します。Advanced Compression オプションのライセンスが必要です。
-- COMPRESS ADVANCED LOW: 標準的な Advanced Index Compression(推奨)
-- ・単一列インデックスにも対応
-- ・一意インデックスにも対応
-- ・標準キー圧縮より効果が高いことが多い
-- ・DML パフォーマンスへの影響は LOW
CREATE INDEX emp_email_idx
ON employees(email)
COMPRESS ADVANCED LOW;
-- 一意インデックスにも適用できる(標準 COMPRESS では制限あり)
CREATE UNIQUE INDEX emp_email_uniq_idx
ON employees(email)
COMPRESS ADVANCED LOW;
-- COMPRESS ADVANCED HIGH: より高い圧縮率(Oracle 18c 以降、PartitionedTableなど特定ケース向け)
-- DML オーバーヘッドが HIGH より大きい
CREATE INDEX sales_date_product_idx
ON sales(sale_date, product_id)
COMPRESS ADVANCED HIGH;
-- Advanced Index Compression のステータスを確認する
SELECT index_name, compression
FROM DBA_INDEXES
WHERE table_name = 'EMPLOYEES';
-- COMPRESSION: 'ADVANCED LOW', 'ADVANCED HIGH', 'ENABLED'(標準), 'DISABLED'(なし)
-- 標準キー圧縮と Advanced 圧縮の使い分け
-- ・非一意複合インデックス + 先行列のカーディナリティが低い → 標準 COMPRESS n が効果的
-- ・一意インデックス or 単一列インデックス → COMPRESS ADVANCED LOW
-- ・Advanced Compression ライセンスなし → 標準 COMPRESS n のみ使用可
既存インデックスを圧縮付きで再構築する
-- 既存インデックスを圧縮付きで再構築する(オンラインで実行可能)
ALTER INDEX hr.emp_dept_job_nocomp_idx
REBUILD COMPRESS 2 ONLINE;
-- ONLINE: テーブルをロックせずに再構築(Enterprise Edition のみ)
-- Advanced 圧縮に切り替える
ALTER INDEX hr.emp_dept_job_idx
REBUILD COMPRESS ADVANCED LOW ONLINE;
-- 圧縮を解除する(非圧縮に戻す)
ALTER INDEX hr.emp_dept_job_idx
REBUILD NOCOMPRESS ONLINE;
-- 再構築後に統計を更新する
EXEC DBMS_STATS.GATHER_INDEX_STATS('HR', 'EMP_DEPT_JOB_IDX');
-- 変更後のリーフブロック数を確認して圧縮効果を測る
SELECT index_name, compression, leaf_blocks, num_rows
FROM DBA_INDEXES
WHERE index_name = 'EMP_DEPT_JOB_IDX';
圧縮効果を事前に測定する
インデックスを実際に再作成する前に、圧縮による効果を見積もることができます。ANALYZE INDEX ... VALIDATE STRUCTURE とINDEX_STATS ビューを使って既存インデックスの状態を確認したり、DBMS_COMPRESSION.GET_COMPRESSION_RATIO(Advanced Compression ライセンスが必要)で推定できます。
-- ANALYZE INDEX: インデックス構造を分析する(INDEX_STATS に結果が格納される)
ANALYZE INDEX hr.emp_dept_job_idx VALIDATE STRUCTURE;
-- INDEX_STATS: 分析結果を確認する(直前の ANALYZE INDEX の結果のみ)
SELECT
name,
height, -- B-Tree の高さ
blocks, -- 総ブロック数
lf_rows, -- リーフ行数
lf_blks, -- リーフブロック数
lf_rows_len, -- リーフ行の総バイト数
br_rows, -- ブランチ行数
br_blks, -- ブランチブロック数
del_lf_rows, -- 削除済みリーフ行数(再利用待ち)
ROUND(del_lf_rows / lf_rows * 100, 1) AS pct_deleted -- 削除済み率(高い場合は REBUILD が有効)
FROM INDEX_STATS
WHERE name = 'EMP_DEPT_JOB_IDX';
-- 削除済み行が多い場合は REBUILD で領域を回収する
-- pct_deleted が 20% 以上ならインデックスの再構築を検討する
ALTER INDEX hr.emp_dept_job_idx REBUILD ONLINE;
-- スキーマ内の全インデックスの圧縮状態を一覧表示する
SELECT
i.index_name,
i.table_name,
i.index_type,
i.uniqueness,
i.compression,
i.leaf_blocks,
s.bytes / 1024 / 1024 AS size_mb
FROM DBA_INDEXES i
LEFT JOIN DBA_SEGMENTS s ON s.segment_name = i.index_name AND s.owner = i.owner
WHERE i.owner = 'HR'
ORDER BY s.bytes DESC NULLS LAST;
インデックス圧縮の注意事項
- 一意インデックス:
COMPRESS nで指定できる n の最大値は(インデックス列数 – 1)。全列を圧縮すると一意性を保証できなくなるため - 単一列インデックス:
COMPRESS 1は単一列インデックスには適用できない(ADVANCED 圧縮を使う) - 圧縮効果がない場合:先行列がほぼユニークな場合(主キーに近い場合)は圧縮効果が低く、むしろオーバーヘッドが発生することがある
- Advanced Compression ライセンス:COMPRESS ADVANCED LOW/HIGH は Oracle Advanced Compression オプションが必要(EE 標準機能ではない)
まとめ
- COMPRESS n:複合インデックスの先頭 n 列を圧縮する。先行列のカーディナリティが低い(繰り返しが多い)ほど効果的
- 効果:インデックスのリーフブロック数が減少 → バッファキャッシュ効率向上 → I/O 削減 → クエリ高速化
- ALTER INDEX REBUILD COMPRESS:既存インデックスを再構築して圧縮を適用する。ONLINE で無停止実行可能
- Advanced Index Compression:Oracle 12c + Advanced Compression オプションが必要。一意インデックス・単一列インデックスにも対応。LOW/HIGH のレベルを選択できる
- DBA_INDEXES.COMPRESSION:ENABLED(標準)/ ADVANCED LOW / ADVANCED HIGH / DISABLED で圧縮状態を確認できる
- ANALYZE INDEX VALIDATE STRUCTURE:インデックスの構造を分析して del_lf_rows(削除済み行)の割合を確認する。高い場合は REBUILD を検討する
インデックスの種類・作成方法・パフォーマンスへの影響の基礎は Oracle インデックス完全ガイドを参照してください。ビットマップインデックスとの使い分けについては ビットマップインデックス完全ガイドも参照してください。