【実務・中級編】 副次インデックス – 階層型DBMS

階層型DBMSにおける「副次インデックス」の極意:ツリーの呪縛を断ち切る設計論

おい、コードレビューの手を止めてくれ。今上がってきたこのスキーマ設計、一体いつの時代の代物だ?

「親セグメントを辿れば全件スキャンになるから、とりあえず全属性にセカンダリインデックス(副次インデックス)を貼りました」――そんな安易な言い訳は、私の前では通用しない。

現代のリレーショナルデータベース(RDBMS)に毒された脳みそで階層型DBMS(IBM IMSや富士通 AIMなど)を触ると、必ず痛い目を見る。階層型モデルは「親から子へ」という一方向のポインタ鎖によって、圧倒的な書き込み性能とデータ凝集性を叩き出す反面、「ルート・セグメントを経由しない単一属性での検索」に対しては極めて冷酷な構造をしている。

今回は、この階層型DBMSの最大の弱点を補い、かつシステムの寿命を縮めないための「副次インデックス(Secondary Index)」の極限の設計論を授けよう。レビューで赤ペンを入れる前に、この本質を脳裏に焼き付けろ。

—

1. 階層型DBMSにおける副次インデックスの構造的真実

まず、大前提を叩き込む。
階層型DBMSのデータは、物理的なディスク上で「親-子」の階層パス(物理ポインタ、あるいはダイレクト・アドレス)によって緊密に結ばれている。主キー(Sequence Field)によるアクセスは、この物理パスを辿るだけなので秒速で終わる。

しかし、業務要件とは非情なものだ。「顧客ID(ルート)」ではなく、「社員の氏名」や「部品のシリアル番号」といった非階層キーで引きたいという要求が必ず飛んでくる。

ここで登場するのが副次インデックス(Secondary Index)だ。

内部構造:ターゲット・セグメントへの「脱出ロープ」

副次インデックスの正体は、リレーショナル界隈でいうB+樹インデックスの概念に近いが、その指し示す先が決定的に異なる。
副次インデックスのリーフノードが保持しているのは、RDBMSのような「行の物理アドレス」ではない。多くの場合、以下のいずれかだ。

1. ターゲット・セグメントの物理アドレス(ダイレクト・ポインタ)
2. ルート・セグメントへのポインタ + 階層パスを辿るためのキー群

[副次インデックス領域]
社員名「Yamada」 ──> [ポインタ] ──> (実データの社員セグメントへジャンプ)

この「ジャンプ」が発生する時点で、階層型DBMS本来の美しい「物理連続アクセス」の恩恵が薄れることをまず理解しろ。副次インデックスとは、階層構造の秩序をあえて乱すための「特例の裏口」なのだ。

—

2. 【実践】DDLとスキーマ定義における設計パターン

実際の定義構文を見てみよう。ここでは概念的なDDL(データ定義言語)を用いて、副次インデックスの定義と、それに伴うペナルティを回避する設計パターンを示す。

悪い例:思考停止の全属性インデックス付与

— 【悪手】なんでもかんでも副次インデックスを貼った悲惨な例
DATABASE EnterpriseDB;
SEGMENT Root (Employee_ID);
— 氏名で検索したいから副次インデックス
SECONDARY INDEX X_Name ON Employee (Name);
— 部署コードで集計したいから副次インデックス
SECONDARY INDEX X_Dept ON Employee (Dept_Code);
— 連絡先電話番号で検索したいから副次インデックス
SECONDARY INDEX X_Phone ON Employee (Phone_Number);

なぜこれがクソなのか?
階層型DBMSにおいて、副次インデックスの更新コストはRDBMSの比ではない。データが更新(INSERT/UPDATE/DELETE)されるたびに、ツリー構造自体のポインタ操作に加え、すべての副次インデックスのツリーを同期させるためのロックとI/Oが発生する。
結果、バッチ処理のスループットが劇的に低下し、「書き込みの遅い階層型」という最悪のレッテルを貼られることになる。

—

正しい例:最小限のインデックスと「ポインタ・ターゲット」の最適化

実務で許容されるのは、「どうしてもルートパスで引けない、かつ参照頻度が圧倒的に高いトランザクションのキー」の1つ、精々2つまでだ。

以下のコードを見てほしい。ここでは、非階層キーからのアクセスを最小限のオーバーヘッドで実現する堅牢な設計を示している。

— 【推奨】必要最小限の副次インデックスとポインタ設計
DATABASE EnterpriseDB;

— ルートセグメント:組織
SEGMENT Organization (Org_Code) {
Org_Name CHAR(50);
}

— 子セグメント:社員(物理的にはOrg_Code配下に格納される)
SEGMENT Employee (Org_Code, Emp_ID) {
Emp_ID CHAR(10) PRIMARY KEY,
Name CHAR(50),
Email CHAR(100),
Status CHAR(1)
}
— 【極限の知見】
— 外部からの「Email」による単体引き当ては業務上クリティカルなため、
— 副次インデックスを許可する。ただし、ターゲットは最小限のデータに絞る。
SECONDARY INDEX X_Emp_Email
ON Employee (Email)
POINTER IS DIRECT
UNIQUE; — 一意制約を持たせることでインデックスツリーの肥大化を防ぐ

この設計のキモ:

  • `POINTER IS DIRECT` の指定: インデックスからデータセグメントへの直接ポインタを維持し、探索時のパス走査コストをゼロにする。
  • `UNIQUE` の強制: 重複がないことを保証し、インデックス内のチェイン(同値キーのリスト)の肥大化を防ぐ。これにより、検索時のスキャンコストをO(1)に近づける。

—

3. パフォーマンス上の注意点:チーフアーキテクトからの警告

副次インデックスを導入する際、お前たちが必ず直面する「3つの地獄」をあらかじめ警告しておく。

① メンテナンス・コスト(UPDATEの罠)

リレーショナルDBであれば、インデックス化されていないカラムの更新はデータ領域の書き換えだけで済む。しかし、階層型DBMSの副次インデックス対象カラム(例えば上記の `Email`)が変更された場合、DBMSは内部で以下の処理を不可避的に行う。
1. 古いインデックスエントリの削除
2. 新しいインデックスエントリの挿入
3. ポインタの付け替え

この処理中、セグメントは排他ロック(Xロック)の保持時間が長くなる。高頻度で更新される属性を副次インデックスのキーにしてはならない。「マスター的性格を持ち、めったに変わらない属性」だけに絞れ。

② ポインタの「腐敗(Stale Pointer)」と再編成

物理ポインタを多用する階層型DBMSでは、データの削除と再挿入が繰り返されると、ポインタチェインが断片化(ストレージのフラグメンテーション)を起こす。
副次インデックス経由のアクセス速度が日を追うごとに低下していると感じたら、それはDBの構造寿命のサインだ。定期的なデータベースのアンロード・リロード(再編成バッチ)を運用カレンダーに組み込むこと。これを忘れるインフラ担当は即座に外せ。

③ 仮想セグメント(Secondary Data Structure)の誘惑に負けるな

一部の高度な階層型DBMSには、副次インデックスの発展形として「仮想的な子セグメント(異なる親を持つように見せかける機能)」が存在する。これを使えば、あたかもリレーショナルのJOINのようにデータを結合して見せることができる。
だが、使うな。
それをやり始めた瞬間、階層型DBMSを採用した最大のメリットである「予測可能な高速性とデータの局所性」が完全にあらぬ方向へ崩壊する。複雑な結合が必要なら、それは階層型の仕事ではなく、データレイクやRDBMSにデータを非同期連携してやらせるべき仕事だ。

—

4. 総括:アーキテクトとしての判断基準

階層型DBMSにおける副次インデックスとは、「本来の美しきツリー構造を汚すための、最小限にして最後の劇薬」だ。

設計レビューにおいて、開発者から「この検索要件があるからインデックスを追加したい」と言われたら、以下の3点を問いただせ。

1. 「その検索は、本当にルートからの階層パスでは解決できないのか?」(設計の怠慢を疑え)
2. 「その属性の更新頻度は、システムの許容するI/O限界値内に収まっているか?」(書き込み性能の劣化を見抜け)
3. 「ポインタの直リンク化や一意制約の付与など、オーバーヘッドを最小化するチューニングが施されているか?」(実装の解度を確認しろ)

この問いに論理的に答えられない設計は、すべて差し戻せ。
我々が扱うのは、システムの根幹を支える重厚長大な基盤だ。その背骨を、安易なインデックスの乱用で歪ませることは絶対に許されない。

さあ、手を動かす時間だ。完璧なスキーマを作って私を唸らせてみせろ。

コメント

タイトルとURLをコピーしました