HISAMの極意:なぜ今、我々はキー順アクセスの原点に立ち返るべきなのか
おい、手を止めてくれ。次の設計レビューに入る前に、少しだけ昔の――いや、データ構造の根源を揺るがす「真実」について話そう。
お前たちは普段、RDBのB-Treeインデックスや、NoSQLのLSMツリーに胡坐をかいていないか? 「とりあえず主キーで引ければいい」「レンジスキャンなんてO(N)でしょ」などと、思考停止したクエリを書いていないか。
違う。我々が対峙しているのは「データ」の本質だ。
特に、親子関係を持ち、かつキー順(Sorted)で高速にシーケンシャルアクセスを完結させなければならないドメインにおいて、現代の汎用DBMSの抽象化層は時に牙を抜かれた子猫のように無力になる。
そこで登場するのが HISAM (Hierarchical Indexed Sequential Access Method) だ。
IBMのIMS(Information Management System)の基盤を支え、現代のハイパフォーマンス・ストレージエンジンの設計思想にも多大な影響を与えたこのアーキテクチャ。その血肉を、ここで徹底的に叩き込む。コードレビューで「なぜこの設計にしたのか」を論理的に説明できない者は、今日から私の下で出直してもらう。
—
1. 階層型DBMSにおける「HISAM」の正体
HISAMは、一言で言えば 「インデックス(索引)による直接アクセス」と「オーバーフロー管理付きのキー順シーケンシャルアクセス」のキメラ だ。
RDBのようにテーブルをバラバラに置き、後からJOINで結合するのではない。HISAMは、親子関係(Segment)をあらかじめ物理的に隣接させて配置する。これが階層型DBMSの真骨頂「物理的親子ポインタの排除(または最小化)」だ。
アーキテクチャの基本構造
HISAMのデータセットは、大きく2つのエリアに分かれる。
1. プライマリ・エリア (Primary Area / 根幹ファイル)
- 論理レコード(Root Segmentを含むツリー)が、ルートキーの昇順で綺麗にパックされている。
- 固定長の論理ブロック(OSのブロック単位)に収まるだけ詰め込まれる。
2. オーバーフロー・エリア (Overflow Area)
- 親セグメントに対して子・孫セグメントが多すぎたり、途中に新規挿入(Insert)が発生してプライマリ・エリアに収まりきらなくなったデータをチェイン(ポインタで接続)して格納する。
[HISAMの物理レイアウト概念図]
+——————————————————-+
| プライマリ・エリア (Primary Area) |
| [Root: Key 010] -> [Child A] -> [Child B] |
| [Root: Key 020] -> [Child A] |
| [Root: Key 030] ———————————-+ |
+—————————————————|—+
|
v (オーバーフローポインタ)
+——————————————————-+
| オーバーフロー・エリア (Overflow Area) |
| [Root 030の溢れたChild C] -> [GrandChild D] |
+——————————————————-+
この構造が意味するものはお分かりか?
「ルートキーの順序で物理的にソートされている」ため、レンジスキャン(範囲検索)時のディスクシークが理論上最小限になる。B-Treeのリーフノードを辿るオーバーヘッドすらない、純粋な連続読み出しが可能になるのだ。
—
2. スキーマ定義(DBD)の実践と解釈
HISAMのスキーマ定義(IMSのDBD:Database Descriptionに相当)を模したDCL(Data Control Language)を見てみよう。ここでは、ECサイトの「顧客(Root)」と「注文履歴(Child)」をHISAMで構築するケースを想定する。
— =================================================================
— [DBD定義模擬] 顧客-注文階層構造のHISAMスキーマ
— =================================================================
— データベース全体の宣言
DATABASE CUSTHDB ACCESS=HISAM
— 1. ルートセグメント(顧客)の定義
— NAME: セグメント名
— BYTES: 固定長サイズ
— PTR: ポインタの持ち方(HISAMでは論理順序を維持)
SEGM NAME=CUSTOMER, BYTES=120, PARENT=0, POINTER=NOTIN
— ルートのユニークキー(これがHISAMの索引キーになる)
FIELD NAME=CUST_ID, SEQ=M, BYTES=10, START=1
— 2. 子セグメント(注文履歴)の定義
— CUSTOMERセグメントの直下に物理配置される
SEGM NAME=ORDER, BYTES=80, PARENT=CUSTOMER, POINTER=TWIN
— 注文日時の順に物理ソートされる設定 (SEQ=M)
FIELD NAME=ORDER_DATE, SEQ=M, BYTES=14, START=1
FIELD NAME=ORDER_ID, BYTES=16, START=15
チーフアーキテクトのコードレビュー
- `SEQ=M` の重み:
ルートの `CUST_ID` も、子の `ORDER_DATE` も `SEQ=M(Ascending/Descending)` が指定されている。これはDBMSに対し、「この順序を物理的に死守せよ」という厳格な命令だ。インサート時にこの順序が崩れる場合、HISAMは自動的にオーバーフロー領域へデータを飛ばす。
- ポインタの最小化 (`POINTER=TWIN` / `NOTIN`):
RDBのようにすべての行に外部キーのインデックス貼るような愚行はしない。物理的な近接性(Locality of Reference)だけで関係性を表現する。これがHISAMの爆発的なスループットの源泉だ。
—
3. 堅牢な設計パターン:なぜHISAMを選ぶべきか
実務の設計において、何でもかんでもRDBやDocument DBで解決しようとする若手が多い。しかし、以下の条件が揃ったシステムでは、HISAM(あるいはその思想を汲んだカスタムストレージエンジン)の採用を強く検討すべきだ。
パターンA:月次・日次で完全なキー順バッチ処理が回るシステム
- 要件: 1億件の顧客データを、ID順に1件残らず舐め回すように集計する。
- HISAMの優位性:
RDBのB-Treeインデックススキャンは、ツリー構造の上下動やランダムI/Oを伴う。しかしHISAMのプライマリ・エリアは、OSのページキャッシュに乗った瞬間、極めて美しいシーケンシャルリードの嵐と化す。I/O効率はRDBの比ではない。
パターンB:親子関係の粒度が「親1に対して子数件〜数十件」で固定的なシステム
- 要件: 顧客と、直近の直近の注文数件。
- HISAMの優位性:
親子が物理的に同じブロック(または連続したブロック)に存在するため、親を引いた瞬間に子もメモリ上にロードされる。「N+1問題」なんて言葉、HISAMの世界には存在しない。概念すらない。
—
4. パフォーマンス上の罠:HISAMの「暗黒面」
勘のいいエンジニアなら気づいているはずだ。「そんな都合の良い話ばかりではないだろう」と。その通りだ。HISAMには致命的な弱点がある。これを理解せずに本番運用に踏み切ったら、半年後にシステムは死に至る。
1. 乱雑な挿入(Random Insertion)による「オーバーフローの嵐」
HISAMの最大の弱点は 「動的なデータ追加(特にキー順の途中への挿入)」 だ。
プライマリ・エリアに空きがない状態で、既存のキーの間に新しいデータが割り込んできた場合、データはオーバーフロー・エリアに飛ばされる。
- 何が起きるか?:
オーバーフローが多発すると、本来シーケンシャルであるはずの読み出しが、ポインタを追うための「ランダムI/O」に変貌する。これを HISAMの劣化(Degradation) と呼ぶ。
- 対策(アーキテクトの鉄則):
- リオーガナイゼーション(Reorg)の定期実行: 定期的にデータベース全体を再編成し、プライマリ・エリアの物理順序を再構築するバッチを必ず運用カレンダーに組み込め。
- フリースペース(Free Space)の適切な確保: DBD定義時に、将来の挿入を見越してブロック内にあらかじめ余白(FSPACE)を残す設計を徹底しろ。
2. 可変長データの扱いと断片化
HISAMは基本的に固定長セグメントの扱いに最適化されている。可変長(Variable-length)を多用すると、パディングによる領域の無駄遣いか、オーバーフロー・エリアの断片化(Fragmentation)を引き起こす。スキーマ設計時には、属性の最大長を見極めるシビアな目が求められる。
—
5. 結びにかえて
HISAMは、レトロな遺物などではない。
「データをどのように物理メモリやストレージに並べれば、CPUとI/Oの効率を極限まで高められるか」という、データベースエンジニアリングの極限の回答の一つだ。
お前たちが次に新しいデータストアを設計するとき、あるいは既存の重いバッチクエリをチューニングするとき、思い出してほしい。
「物理的な配置(Locality)が、アルゴリズムの複雑さを凌駕する」という事実を。
理論武装を怠るな。コードを書け、そして物理レイアウトを想像しろ。
次のレビューで、お前たちの洗練された設計が見られるのを楽しみにしている。解散!
コメント