【完全ガイド】ORA-08102: index key not found の原因と解決方法|インデックス破損・REBUILD の罠 徹底解説

【完全ガイド】ORA-08102: index key not found の原因と解決方法|インデックス破損・REBUILD の罠 徹底解説

Oracle DBA が本番運用中に突然遭遇する深刻なインデックス破損エラー:

DELETE FROM limits WHERE acct_id = 1496605012;
*
ERROR at line 1:
ORA-08102: index key not found, obj# 7941, file 11, block 398468 (2)

**「インデックスにキーが見つからない」**というシンプルなメッセージ。インデックスとテーブルの深刻な不整合を示す重大エラーです:

  • インデックスに登録されるべきキーが物理的に欠落
  • テーブルとインデックスのメタデータ不整合
  • DML 全停止(特に DELETE / UPDATE)
  • DBA 緊急対応必須
  • 単純な REBUILD では解決しない場合が多い

このエラーの本質は、Rizwan’s Oracle Blog が明確に述べています:

「インデックスに格納されたキーと、テーブルに格納された値の間に
  ミスマッチが発生している状態」
目次

エラーメッセージの詳細

Oracle 10g+:

ORA-08102: index key not found, obj# 7941, file 11, block 398468 (2)
                                obj#      file     block
                                対象インデックス オブジェクト

obj# から破損オブジェクト特定:

SELECT owner, object_name, object_type
FROM dba_objects
WHERE object_id = 7941;

REBUILD ONLINE vs OFFLINE の重大な違い

日本語圏でほぼ知られていない重要な事実:

ALTER INDEX ... REBUILD (OFFLINE):
  → 既存インデックスから新インデックスを構築
  → 破損キーもそのままコピー → 治らない!

ALTER INDEX ... REBUILD ONLINE:
  → テーブルから直接読み取って新インデックス構築
  → 破損キーが正しく修復
  → 多くの場合これで解決

DROP + CREATE INDEX:
  → 完全新規作成
  → 最も確実な解決方法

Rizwan の実例:

1. REBUILD (OFFLINE) 試行 → 失敗
2. DROP + CREATE INDEX → 成功

Mohamed Houri の実例:

1. REBUILD (OFFLINE) 試行 → 失敗
2. ALTER TABLE MOVE → 全インデックス UNUSABLE
3. REBUILD 実行 → 成功
   (UNUSABLE 状態からの REBUILD はテーブルから読み取り)

現場で最も典型的なパターン:

  • Oracle 内部バグ(多数の既知バグ)
  • パラレル DML 中の corruption
  • ストレージ障害(RAID、SAN、NAS)
  • システムクラッシュ中の DML
  • Function-Based Index の関数変更後
  • グローバル/ローカルインデックスの整合性問題(パーティション操作)
  • SAP BI アップグレード中(実例多数)
  • パーティションメンテナンス中
  • Multi-user 同時 DML での競合
  • Data Pump インポート後

さらに、併発する典型的なエラー:

ORA-00604: error occurred at recursive SQL level 1
ORA-08102: index key not found, obj# 246739, file 68, block 78323 (2)

recursive SQL レベルでの発生も多く、内部処理が影響を受けます。

多くの日本語記事が「REBUILD せよ」で終わりますが、実務では:

  • REBUILD の 3 モード(OFFLINE / ONLINE / UNUSABLE→REBUILD)の厳密な違い
  • ANALYZE INDEX ... VALIDATE STRUCTURE による事前診断
  • INDEX_STATS ビュー活用
  • DBVERIFY (dbv) による物理破損チェック
  • Function-Based Index関数不変性違反
  • グローバルインデックスのパーティション操作影響
  • SAP brtools による対処
  • Rails ヘルスチェックでの予防
  • ALTER TABLE MOVE による強制的な整合性回復
  • リカバリからの復元(最終手段)

さらに、Oracle のマルチバージョンでの対応:

9i or Earlier: ORA-08102: index key not found, obj# %s, dba %s (%s)
10g+:          ORA-08102: index key not found, obj# %s, file %s, block %s (%s)

本記事では、ORA-08102: index key not found完全な原因と解決方法を、リファレンスとして実用的に整理します。REBUILD の罠、10大発生パターン、6つの解決策、SAP 対応、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-08102 に冷静に的確に対処できるようになります。


結論:REBUILD ONLINE or DROP + CREATE

時間がない方向けに、最速の対処を先に示します。

エラーメッセージの読み方

ORA-08102: index key not found, obj# 7941, file 11, block 398468 (2)
                                obj#       file    block
                                ↑         ↑       ↑
                          破損オブジェクト  データファイル  ブロック

= インデックスに存在すべきキーが物理的に欠落
  テーブル-インデックス不整合

最速の対処手順

-- STEP 1: obj# から破損オブジェクト特定
SELECT owner, object_name, object_type
FROM dba_objects
WHERE object_id = <obj#>;

-- STEP 2: REBUILD ONLINE 試行(テーブルから再構築)
ALTER INDEX schema.index_name REBUILD ONLINE;

-- STEP 3: 失敗なら UNUSABLE + REBUILD
ALTER INDEX schema.index_name UNUSABLE;
ALTER INDEX schema.index_name REBUILD;

-- STEP 4: 最終手段: DROP + CREATE
-- DDL 取得
SELECT DBMS_METADATA.GET_DDL('INDEX', 'INDEX_NAME', 'OWNER')
FROM dual;

-- DROP + CREATE
DROP INDEX schema.index_name;
CREATE INDEX ...;

6つの解決策

#手法使う場面
REBUILD ONLINE第一選択
UNUSABLE + REBUILDONLINE 失敗時
DROP + CREATE最も確実
ALTER TABLE MOVE全 index リビルド
ANALYZE VALIDATE診断
DBVERIFY物理チェック

REBUILD の 3 モード

OFFLINE REBUILD:
  既存インデックス → 新インデックス
  破損コピー、治らない

ONLINE REBUILD:
  テーブル → 新インデックス
  破損修復可能

UNUSABLE → REBUILD:
  UNUSABLE 状態から
  必ずテーブルから再構築
  破損修復可能

詳細は以下で解説します。


Oracle インデックスの構造

インデックスの物理構造

インデックスセグメント
├── ルートブロック
├── ブランチブロック
├── リーフブロック
│   ├── キー値 → ROWID (テーブル位置)
│   └── 順序付き

インデックス-テーブル整合性

通常状態:

インデックスキー ↔ テーブル値
1:1 対応
Oracle が DML で自動維持

ORA-08102 状態:

インデックスに登録されるべきキー欠落
または
テーブル値とインデックスキーの不一致

→ Oracle が矛盾を検出
→ ORA-08102 発生

破損の種類

論理的破損(本記事の主対象):

  • キーの欠落
  • キー-値の不一致
  • 通常 REBUILD ONLINE / DROP + CREATE で修復

物理的破損(別問題):

  • ブロック corruption
  • ORA-01578 発生
  • DBVERIFY / RMAN BLOCKRECOVER で対処

【原因①】Oracle 内部バグ(最多)

症状

本番運用中に突然発生
特定の DML(DELETE 等)で ORA-08102
再現条件が不明瞭

対処

A. Oracle Support 案件化 + パッチ確認:

- MOS で類似バグ検索
- 該当バグの patch 適用
- 最新 PSU / CPU 適用

B. 応急処置: DROP + CREATE:

DROP INDEX schema.idx_name;
CREATE INDEX schema.idx_name ON tab(col);

内部エラーは ORA-00600: internal error の記事も参照してください。


【原因②】パラレル DML 中の corruption

シナリオ

ALTER SESSION ENABLE PARALLEL DML;

DELETE /*+ PARALLEL(orders 8) */ FROM orders 
WHERE order_date < DATE '2020-01-01';

-- パラレル実行中のインデックスメンテナンス競合
-- → 稀に corruption

対処

REBUILD ONLINE + パラレル度制限:

-- パラレル DML 無効化
ALTER SESSION DISABLE PARALLEL DML;

-- インデックス REBUILD
ALTER INDEX orders_idx REBUILD ONLINE;

【原因③】ストレージ障害

シナリオ

alert.log:
"I/O error on file 11 block 398468"
その後
"ORA-08102: index key not found, obj# 7941, file 11, block 398468"

診断

A. OS レベル確認:

$ dmesg | grep -E "(I/O error|read error)"
$ smartctl -a /dev/sda

B. RMAN BLOCKRECOVER:

rman target /
RMAN> BACKUP VALIDATE DATAFILE 11;
RMAN> RECOVER DATAFILE 11 BLOCK 398468;

C. インデックス再作成:

DROP INDEX schema.idx_name;
CREATE INDEX schema.idx_name ON tab(col);

破損関連は ORA-01578: data block corrupted の記事、ORA-00376: file cannot be read の記事も参照してください。


【原因④】システムクラッシュ中の DML

シナリオ

1. 大量 DML 実行中
2. 電源断 or OS クラッシュ
3. 再起動後
4. Instance Recovery 実行
5. 稀に index 不整合残存
6. → ORA-08102

対処

REBUILD ONLINE:

ALTER INDEX schema.idx_name REBUILD ONLINE;

失敗なら DROP + CREATE


【原因⑤】Function-Based Index の関数変更

シナリオ

-- 関数を使ったインデックス
CREATE INDEX idx_upper ON emp (UPPER(name));

-- カスタム関数
CREATE FUNCTION my_upper(v VARCHAR2) RETURN VARCHAR2 
DETERMINISTIC AS
BEGIN
  RETURN UPPER(v);
END;
/

CREATE INDEX idx_myupper ON emp (my_upper(name));

-- 関数を変更(DETERMINISTIC 違反)
CREATE OR REPLACE FUNCTION my_upper(v VARCHAR2) RETURN VARCHAR2 
DETERMINISTIC AS
BEGIN
  RETURN LOWER(v);   -- ⚠️ 別の結果!
END;
/

-- インデックス不整合
-- → ORA-08102

対処

関数のロジックを元に戻す or インデックス再作成:

DROP INDEX idx_myupper;
CREATE INDEX idx_myupper ON emp (my_upper(name));

【原因⑥】グローバル/ローカルインデックス整合性

シナリオ(実例)

-- ローカルインデックス操作
ALTER TABLE ENTITY_ALT_IDENTIFIER 
  MODIFY PARTITION P_59221_1000_DL UNUSABLE LOCAL INDEXES;

-- INSERT
INSERT INTO ENTITY_ALT_IDENTIFIER VALUES (...);

-- ローカルインデックス REBUILD
ALTER TABLE ENTITY_ALT_IDENTIFIER 
  MODIFY PARTITION P_59221_1000_DL REBUILD UNUSABLE LOCAL INDEXES;

-- TRUNCATE + UPDATE INDEXES
ALTER TABLE ENTITY_ALT_IDENTIFIER 
  TRUNCATE PARTITION P_59221_1000_DL UPDATE INDEXES;

-- グローバル INDEX も UNUSABLE に
-- → 通常業務で ORA-08102

解決

-- グローバル INDEX を明示的に REBUILD
ALTER INDEX IDX_ENTITY_ALT_IDEN_PKEY REBUILD ONLINE;

インデックス関連は ORA-01502: index unusable の記事、ORA-14400: inserted partition key does not map の記事も参照してください。


【原因⑦】SAP BI アップグレード(実例)

症状(実例)

SAP BI 3.5 → BI 7.0 アップグレード中
TABIM_UPG フェーズで ORA-08102

対処(SAP 公式)

A. brtools 使用:

brtools -> 3. Segment management -> 2. Rebuild indexes -> 7. Index name -> continue

B. インデックス REBUILD:

ALTER INDEX <index_name> REBUILD ONLINE;

C. 全インデックス再構築:

SELECT 'ALTER INDEX ' || index_name || ' REBUILD ONLINE;'
FROM user_indexes
WHERE table_name IN ('AFFECTED_TABLE');

【原因⑧】パーティションメンテナンス中

シナリオ

-- パーティション DDL 中断
ALTER TABLE orders EXCHANGE PARTITION p_2024 WITH TABLE archive_2024;
-- 何らかの理由で失敗

-- グローバル/ローカル INDEX 不整合
-- → ORA-08102

解決

UPDATE INDEXES 付き実行:

ALTER TABLE orders EXCHANGE PARTITION p_2024 WITH TABLE archive_2024
  UPDATE INDEXES;

失敗時: 全 INDEX REBUILD:

BEGIN
  FOR r IN (SELECT index_name FROM user_indexes 
            WHERE table_name = 'ORDERS' AND status IN ('UNUSABLE', 'INVALID')) LOOP
    EXECUTE IMMEDIATE 
      'ALTER INDEX ' || r.index_name || ' REBUILD ONLINE';
  END LOOP;
END;
/

【原因⑨】Multi-user 同時 DML

シナリオ

複数セッションが同じインデックスに対して DML 実行
稀な競合状態
→ index-table 不整合
→ ORA-08102

対処

A. インデックス REBUILD ONLINE:

ALTER INDEX schema.idx_name REBUILD ONLINE;

B. アプリ側で serialization 検討。


【原因⑩】Data Pump インポート後

シナリオ

$ impdp scott/tiger dumpfile=orders.dmp
-- インポート成功

-- 業務で ORA-08102

対処

インポート後の全 INDEX 検証・REBUILD:

-- INDEX 検証
BEGIN
  FOR r IN (SELECT index_name FROM user_indexes) LOOP
    BEGIN
      EXECUTE IMMEDIATE 
        'ANALYZE INDEX ' || r.index_name || ' VALIDATE STRUCTURE';
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Corrupt: ' || r.index_name);
    END;
  END LOOP;
END;
/

Data Pump 関連は Oracle Data Pump 使い方の記事も参照してください。


診断ツール完全リファレンス

obj# から特定

SELECT owner, object_name, object_type, status
FROM dba_objects
WHERE object_id = <obj#>;

file / block からセグメント特定

SELECT owner, segment_name, segment_type, tablespace_name
FROM dba_extents
WHERE file_id = <file>
  AND <block> BETWEEN block_id AND block_id + blocks - 1;

ANALYZE INDEX VALIDATE STRUCTURE

-- インデックス構造検証
ANALYZE INDEX schema.idx_name VALIDATE STRUCTURE;

-- 結果は INDEX_STATS に
SELECT height, blocks, name, del_lf_rows_len, distinct_keys
FROM index_stats;

INDEX_STATS

-- ANALYZE 実行後、単一行
SELECT * FROM index_stats;

DBVERIFY (dbv)

# データファイルの物理チェック
$ dbv file=/u01/oradata/orcl/users01.dbf blocksize=8192

# 出力
Total Pages Examined         : XXX
Total Pages Processed (Data) : XXX
Total Pages Failing   (Data) : 0  ← 0 なら OK
Total Pages Marked Corrupt   : 0

V$DATABASE_BLOCK_CORRUPTION

-- ブロック破損一覧
SELECT * FROM v$database_block_corruption;

DDL 取得

SET LONG 999999
SELECT DBMS_METADATA.GET_DDL('INDEX', 'IDX_NAME', 'SCHEMA')
FROM dual;

6つの解決策 完全リファレンス

解決策① ALTER INDEX REBUILD ONLINE(第一選択)

-- テーブルから直接読み取って再構築
ALTER INDEX schema.idx_name REBUILD ONLINE;

-- 並列 + ONLINE
ALTER INDEX schema.idx_name REBUILD ONLINE PARALLEL 4;

メリット:

  • テーブルから読み取るため破損修復可能
  • 業務停止不要

注意:

  • 若干の負荷 + 一時領域増加

解決策② UNUSABLE + REBUILD

-- STEP 1: UNUSABLE 化
ALTER INDEX schema.idx_name UNUSABLE;

-- STEP 2: REBUILD(テーブルから)
ALTER INDEX schema.idx_name REBUILD;

なぜ効くか:

  • UNUSABLE 状態からの REBUILD はテーブルから読み取り
  • 通常の OFFLINE REBUILD と異なる挙動
  • ONLINE と同等の修復効果

解決策③ DROP + CREATE(最も確実)

-- STEP 1: DDL 取得
SET LONG 999999
SELECT DBMS_METADATA.GET_DDL('INDEX', 'IDX_NAME', 'SCHEMA') FROM dual;

-- STEP 2: DROP
DROP INDEX schema.idx_name;

-- STEP 3: CREATE
CREATE INDEX schema.idx_name ON tab(col);

-- または元の DDL で再作成

最も確実だが:

  • 一時的にインデックスなし
  • SELECT パフォーマンス激低下
  • PRIMARY KEY / UNIQUE 制約の場合は制約先に DROP

解決策④ ALTER TABLE MOVE(全インデックス影響)

-- テーブル MOVE で全インデックス UNUSABLE
ALTER TABLE schema.tab_name MOVE;

-- 全インデックス REBUILD
BEGIN
  FOR r IN (SELECT index_name FROM user_indexes 
            WHERE table_name = 'TAB_NAME'
              AND status = 'UNUSABLE') LOOP
    EXECUTE IMMEDIATE 
      'ALTER INDEX ' || r.index_name || ' REBUILD ONLINE';
  END LOOP;
END;
/

メリット: 一発で全インデックス修復。 注意: テーブルサイズ大きいと時間かかる。

解決策⑤ ANALYZE INDEX VALIDATE STRUCTURE(診断)

-- 破損検出
ANALYZE INDEX schema.idx_name VALIDATE STRUCTURE;
-- エラー発生なら破損確定

-- 全インデックス検証
BEGIN
  FOR r IN (SELECT index_name FROM user_indexes) LOOP
    BEGIN
      EXECUTE IMMEDIATE 
        'ANALYZE INDEX ' || r.index_name || ' VALIDATE STRUCTURE';
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Corrupt: ' || r.index_name || ' - ' || SQLERRM);
    END;
  END LOOP;
END;
/

解決策⑥ DBVERIFY(物理チェック)

# データファイルの物理破損チェック
$ dbv file=/u01/oradata/orcl/users01.dbf blocksize=8192

# インデックスのあるデータファイル特定
-- インデックスの datafile 特定
SELECT DISTINCT file_id FROM dba_extents
WHERE segment_name = 'IDX_NAME'
  AND owner = 'SCHEMA';

Rails / Java / Python 対応

Rails ActiveRecord

エラーハンドリング:

begin
  Order.where(id: 12345).destroy_all
rescue ActiveRecord::StatementInvalid => e
  if e.message.include?("ORA-08102")
    Rails.logger.fatal "Index corruption: #{e.message}"
    NotifyOps.critical("DBA action required - index rebuild")
    # 自動修復スクリプト起動 or 手動対応
  end
end

マイグレーション後の検証:

class VerifyIndexes < ActiveRecord::Migration[8.0]
  def up
    execute <<-SQL
      BEGIN
        FOR r IN (SELECT index_name FROM user_indexes) LOOP
          BEGIN
            EXECUTE IMMEDIATE 
              'ANALYZE INDEX ' || r.index_name || ' VALIDATE STRUCTURE';
          EXCEPTION
            WHEN OTHERS THEN
              RAISE_APPLICATION_ERROR(-20001, 
                'Corrupt index: ' || r.index_name);
          END;
        END LOOP;
      END;
    SQL
  end
end

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事も参照してください。

Java (JDBC)

try {
    ps.executeUpdate();
} catch (SQLException e) {
    if (e.getErrorCode() == 8102) {
        logger.fatal("Index corruption: " + e.getMessage());
        alertingService.critical("DBA action required");
        
        // obj# 抽出
        Pattern p = Pattern.compile("obj# (\\d+)");
        Matcher m = p.matcher(e.getMessage());
        if (m.find()) {
            logger.error("Corrupt object ID: " + m.group(1));
        }
    }
}

Python (oracledb)

import oracledb
import re
import logging

try:
    cursor.execute(sql, params)
except oracledb.DatabaseError as e:
    error_obj, = e.args
    if error_obj.code == 8102:
        # obj#, file, block 抽出
        match = re.search(
            r'obj# (\d+), file (\d+), block (\d+)', 
            error_obj.message
        )
        if match:
            obj_id, file_id, block_id = match.groups()
            logging.critical(
                f"Index corrupt: obj#{obj_id}, file{file_id}, block{block_id}"
            )

実践シナリオ

シナリオ1:本番緊急対応

-- 1. エラー詳細取得
-- ORA-08102: index key not found, obj# 7941, file 11, block 398468 (2)

-- 2. obj# から特定
SELECT owner, object_name, object_type
FROM dba_objects WHERE object_id = 7941;
-- APP.ORDERS_IDX (INDEX)

-- 3. REBUILD ONLINE 試行
ALTER INDEX APP.ORDERS_IDX REBUILD ONLINE;

-- 4. 失敗なら UNUSABLE + REBUILD
ALTER INDEX APP.ORDERS_IDX UNUSABLE;
ALTER INDEX APP.ORDERS_IDX REBUILD;

-- 5. さらに失敗なら DROP + CREATE
SELECT DBMS_METADATA.GET_DDL('INDEX', 'ORDERS_IDX', 'APP') FROM dual;
DROP INDEX APP.ORDERS_IDX;
CREATE INDEX APP.ORDERS_IDX ON APP.ORDERS(order_id);

-- 6. 動作確認
DELETE FROM APP.ORDERS WHERE id = 12345;

シナリオ2:全インデックス検証

-- 定期メンテナンス
BEGIN
  FOR r IN (SELECT owner, index_name FROM dba_indexes 
            WHERE owner = 'APP') LOOP
    BEGIN
      EXECUTE IMMEDIATE 
        'ANALYZE INDEX ' || r.owner || '.' || r.index_name || 
        ' VALIDATE STRUCTURE';
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('CORRUPT: ' || r.owner || '.' || r.index_name);
    END;
  END LOOP;
END;
/

シナリオ3:SAP BI アップグレード対応

# brtools 使用
brtools

# メニュー:
# 3. Segment management
#   2. Rebuild indexes
#     7. Index name
#       [対象インデックス名]
#     continue

または SQL 直接:

-- 影響テーブル特定(TP trace から)
-- 該当テーブルの全インデックス REBUILD
BEGIN
  FOR r IN (SELECT index_name FROM user_indexes 
            WHERE table_name = 'AFFECTED_TABLE') LOOP
    EXECUTE IMMEDIATE 
      'ALTER INDEX ' || r.index_name || ' REBUILD ONLINE';
  END LOOP;
END;
/

シナリオ4:CI/CD デプロイ後の検証

- name: Post-deploy index validation
  run: |
    sqlplus -s $DB_USER/$DB_PW <<EOF
    WHENEVER SQLERROR EXIT SQL.SQLCODE
    DECLARE
      v_corrupt NUMBER := 0;
    BEGIN
      FOR r IN (SELECT index_name FROM user_indexes 
                WHERE status = 'VALID') LOOP
        BEGIN
          EXECUTE IMMEDIATE 
            'ANALYZE INDEX ' || r.index_name || ' VALIDATE STRUCTURE';
        EXCEPTION
          WHEN OTHERS THEN
            v_corrupt := v_corrupt + 1;
        END;
      END LOOP;
      IF v_corrupt > 0 THEN
        RAISE_APPLICATION_ERROR(-20001, 'Corrupt indexes: ' || v_corrupt);
      END IF;
    END;
    /
    EOF

Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。

シナリオ5:Docker Oracle でのテスト

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
CREATE TABLE t (id NUMBER PRIMARY KEY, val VARCHAR2(100));
INSERT INTO t VALUES (1, 'test1');
INSERT INTO t VALUES (2, 'test2');
COMMIT;

-- 通常はエラーなし
SELECT * FROM t WHERE id = 1;
DELETE FROM t WHERE id = 1;

-- ANALYZE で検証
ANALYZE INDEX SYS_C0011234 VALIDATE STRUCTURE;
SELECT height, blocks FROM index_stats;
EOF

Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。

シナリオ6:Function-Based Index の再作成

-- 関数変更検出
ALTER INDEX idx_myupper INVISIBLE;

-- 状態確認(VALIDATE でも検出できない場合あり)
DROP INDEX idx_myupper;
CREATE INDEX idx_myupper ON emp (my_upper(name));

-- INVISIBLE 解除
-- 実際は DROP CREATE で新規、INVISIBLE は不要

シナリオ7:ALTER TABLE MOVE パターン

-- 1. 全インデックス確認
SELECT index_name, status FROM user_indexes 
WHERE table_name = 'ORDERS';

-- 2. テーブル MOVE
ALTER TABLE orders MOVE;

-- 3. 全インデックス UNUSABLE 化
SELECT index_name, status FROM user_indexes 
WHERE table_name = 'ORDERS';
-- 全て UNUSABLE

-- 4. 全 REBUILD
BEGIN
  FOR r IN (SELECT index_name FROM user_indexes 
            WHERE table_name = 'ORDERS' AND status = 'UNUSABLE') LOOP
    EXECUTE IMMEDIATE 
      'ALTER INDEX ' || r.index_name || ' REBUILD ONLINE';
  END LOOP;
END;
/

インデックス関連は ORA-01502: index unusable の記事も参照してください。

シナリオ8:Python 監視スクリプト

import oracledb
import logging

def validate_all_indexes(cursor, owner):
    cursor.execute("""
        SELECT index_name FROM dba_indexes 
        WHERE owner = :1 AND status = 'VALID'
    """, [owner.upper()])
    
    indexes = cursor.fetchall()
    corrupt = []
    
    for (idx_name,) in indexes:
        try:
            cursor.execute(
                f"ANALYZE INDEX {owner}.{idx_name} VALIDATE STRUCTURE"
            )
        except oracledb.DatabaseError as e:
            error_obj, = e.args
            corrupt.append({
                'name': idx_name,
                'error': error_obj.message
            })
            logging.error(f"Corrupt: {idx_name}")
    
    return corrupt

シナリオ9:Rails ヘルスチェック

class IndexHealthController < ApplicationController
  def check
    corrupt = []
    
    ActiveRecord::Base.connection.execute(<<-SQL).each do |row|
      SELECT index_name FROM user_indexes WHERE status = 'VALID'
    SQL
      idx_name = row['index_name']
      begin
        ActiveRecord::Base.connection.execute(
          "ANALYZE INDEX #{idx_name} VALIDATE STRUCTURE"
        )
      rescue ActiveRecord::StatementInvalid
        corrupt << idx_name
      end
    end
    
    if corrupt.any?
      render json: { status: 'CRITICAL', corrupt_indexes: corrupt }, 
             status: 503
    else
      render json: { status: 'OK' }
    end
  end
end

シナリオ10:Autonomous DB

Autonomous DB では:
- 自動メンテナンスあり
- 通常 ORA-08102 稀
- 発生時は OCI Console でチケット
- 手動 REBUILD も可能

トラブルシューティング

REBUILD ONLINE 失敗

UNUSABLE + REBUILD、または DROP + CREATE

DROP INDEX で PRIMARY KEY エラー

制約先に DROP:

ALTER TABLE tab DROP CONSTRAINT pk_name;
CREATE UNIQUE INDEX pk_name ON tab(col);
ALTER TABLE tab ADD CONSTRAINT pk_name PRIMARY KEY (col) 
  USING INDEX pk_name;

ANALYZE VALIDATE STRUCTURE でエラー

破損確定、REBUILD ONLINE or DROP + CREATE。

頻繁に発生

Oracle Support 案件化、パッチ確認、ストレージ検査。

バックアップから復元検討

深刻な破損 + バックアップあり → RMAN で該当データファイル復元。

PostgreSQL からの移行

PG は REINDEX、Oracle は ALTER INDEX REBUILD + オンライン差。


よくある質問(FAQ)

Q1. REBUILD (OFFLINE) が効かない理由

既存インデックスから再構築、破損もコピー。ONLINE or UNUSABLE + REBUILD で解決。

Q2. DROP + CREATE のリスク

一時的にインデックスなし、SELECT 激低下、制約先処理必要。

Q3. ALTER TABLE MOVE の効果

全インデックス UNUSABLE、REBUILD で修復。

Q4. ANALYZE VALIDATE STRUCTURE の副作用

排他ロック、業務中は要注意。

Q5. Function-Based Index の問題

関数 DETERMINISTIC 違反、DROP + CREATE 必須。

Q6. Rails での対応

エラー検出 + DBA 通知、自動化スクリプト。

Q7. Java での対応

errorCode == 8102 検出、監視統合。

Q8. Python での対応

oracledb.DatabaseError.code == 8102 + obj#/file/block 抽出。

Q9. パフォーマンス影響

業務停止級、DML 完全停止。

Q10. Oracle Support への報告

MOS チケット、パッチ確認、バグ番号追跡。

Q11. 予防策

  • 定期 ANALYZE VALIDATE
  • パッチ適用
  • ストレージ監視
  • 監視スクリプト

Q12. Autonomous DB での挙動

自動メンテナンスあり、稀に発生。


参考リンク

Oracle 公式


まとめ

ORA-08102: index key not found の要点を再整理します。

エラーの本質

インデックスに登録されるべきキーが物理的に欠落
→ テーブル-インデックス不整合
→ DML 全停止
→ DBA 緊急対応
→ REBUILD ONLINE / DROP CREATE で修復

エラーメッセージの読み方

ORA-08102: index key not found, obj# 7941, file 11, block 398468 (2)
                                obj#       file    block
                                ↑         ↑       ↑
                          破損オブジェクト  データファイル  ブロック

REBUILD の 3 モード

モード動作修復効果
REBUILD (OFFLINE)既存 index から効かない
REBUILD ONLINEテーブルから修復可能
UNUSABLE + REBUILDテーブルから修復可能

10大原因

#原因対処
Oracle 内部バグSupport + パッチ
パラレル DMLONLINE REBUILD
ストレージ障害HW 検査 + REBUILD
システムクラッシュREBUILD ONLINE
Function-BasedDROP + CREATE
パーティション整合性UPDATE INDEXES
SAP BI アップグレードbrtools / REBUILD
パーティションメンテUPDATE INDEXES
Multi-user 同時 DMLONLINE REBUILD
Data Pump 後VALIDATE + REBUILD

6つの解決策

-- ① REBUILD ONLINE(第一選択)
ALTER INDEX idx_name REBUILD ONLINE;

-- ② UNUSABLE + REBUILD
ALTER INDEX idx_name UNUSABLE;
ALTER INDEX idx_name REBUILD;

-- ③ DROP + CREATE(最も確実)
DROP INDEX idx_name;
CREATE INDEX idx_name ON tab(col);

-- ④ ALTER TABLE MOVE
ALTER TABLE tab MOVE;
-- 全 index UNUSABLE → REBUILD

-- ⑤ ANALYZE VALIDATE
ANALYZE INDEX idx_name VALIDATE STRUCTURE;

-- ⑥ DBVERIFY
$ dbv file=/path/to/datafile.dbf

診断コマンド Top 5

-- ① obj# から特定
SELECT owner, object_name FROM dba_objects WHERE object_id = <obj#>;

-- ② file/block からセグメント
SELECT segment_name FROM dba_extents 
WHERE file_id = <f> AND <b> BETWEEN block_id AND block_id + blocks - 1;

-- ③ ANALYZE 検証
ANALYZE INDEX idx VALIDATE STRUCTURE;

-- ④ INDEX_STATS
SELECT * FROM index_stats;

-- ⑤ DBVERIFY (bash)
dbv file=/path blocksize=8192

各言語での対応

Rails:  StatementInvalid + errorCode 8102、監視統合
Java:   errorCode == 8102、obj# 抽出
Python: oracledb.DatabaseError.code == 8102、正規表現で詳細抽出
共通:   DBA 即通知、自動修復スクリプト

予防のポイント

1. 定期 ANALYZE INDEX VALIDATE STRUCTURE
2. Oracle パッチ定期適用
3. ストレージ監視(SMART/RAID)
4. UPS で電源保護
5. パーティション DDL は UPDATE INDEXES
6. Function-Based は DETERMINISTIC 遵守
7. パラレル DML 慎重運用
8. CI/CD で post-deploy 検証
9. Rails/Java/Python でヘルスチェック
10. Autonomous DB は自動メンテナンス活用

これらの知識は、Oracle DBA の本番運用・障害対応・データ整合性・Rails / Java / Python アプリ運用・SAP + Oracle 管理・CI/CD パイプライン・パーティション管理・監視設計・パッチ管理など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-08102 に出会っても冷静に的確に対処できるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。