いいか、RDB(関係データベース)のSQLとインデックス自動最適化に慣れきった頭で階層型DBMSの設計に挑むと、確実に本番環境を沈めることになる。
階層型DBMS、特にIMSに代表されるシステムの心臓部であるHISAM(Hierarchical Indexed Sequential Access Method:階層索引順次アクセス手法)を真に乗りこなすには、SQLの抽象レイヤーを剥ぎ取り、ディスク物理セクター、VSAMのデータセット構造、そしてメモリバッファが織りなす「物理レイアウトの幾何学」をミリバイト単位で制御しなければならない。
現代の分散KVSやLSM-Tree、あるいはBigtableライクなワイドカラムストアの設計思想の源流は、すべてこのHISAMやHDAMの物理構造にある。今回は、私が長年の極限ミッションクリティカルシステム開発で培ってきた、HISAMの物理構造の本質、DBD(Database Description)による厳密なスキーマ定義、そしてパフォーマンスを限界まで絞り出すための設計極意を伝授する。
心して読んでほしい。
—
1. HISAMの物理構造:KSDSとESDSのハイブリッド幾何学
HISAMの最大の特徴は、「ルートセグメントへの高速なランダムアクセス(索引)と、それに紐づく全従属セグメントの物理的連続性の両立」にある。これを実現するために、HISAMはメインのKSDS(Key Sequenced Data Set)と、溢れ(オーバーフロー)領域としてのESDS(Entry Sequenced Data Set)という2つの物理ファイルをペアで運用する。
この物理構造を脳内に叩き込んでほしい。
【KSDS(主データセット)】
+———————————————————————–+
| [Root A (Key: 001)] | [Dep A-1] | [Dep A-2] | Pointer to ESDS ——–+
+———————————————————————–+ |
| (物理チェーン)
【ESDS(オーバーフロー・データセット)】 |
+———————————————————————–+ |
| [Dep A-3] | [Dep A-4] | Pointer to Next ESDS <---------------------------+
+-----------------------------------------------------------------------+
HISAMの物理格納ルール
1. ルートセグメントは、常にKSDSに格納される。KSDSはキー(ルートのシーケンシャルキー)によってインデックスが構築されているため、任意のルートへは瞬時に直接アクセス(Direct Access)できる。
2. ルートにぶら下がる従属(Dependent)セグメントは、階層の「先行順探索(PreorderTraversal:親→左の子→右の子)」の順序に従って、KSDS内の同一論理レコード(LRECL)に物理的に隙間なく詰め込まれる。
3. 従属セグメントが多すぎて、あらかじめ設定したKSDSのLRECLに収まりきらない場合、溢れたセグメントはESDSに順次格納され、KSDSの末尾からポインタ(RBA:相対バイトアドレス)でチェーンされる。
この構造が意味するのは、「データが少ない、あるいはきれいに収まっている状態では、1回のI/Oでルートから末尾の従属セグメントまで一括でメモリ上にロードできる」という圧倒的な局所性(Locality)だ。これがHISAMの生命線である。
—
2. 実践:DBD(スキーマ定義言語)によるHISAMの厳密な定義
では、実際のIMS DBにおけるDDLであるDBD(Database Description)のコードを見ながら、どのようにこの物理構造を制御するのかを解説する。
ここでは、顧客(CUSTOMER)をルートとし、その下に注文(ORDER)、注文明細(ITEM)を配置する典型的な階層構造を定義する。
- DATABASE DESCRIPTION FOR CUSTOMER ORDER SYSTEM (HISAM)
DBD NAME=CUSTDB,ACCESS=HISAM
- [データセットの定義]
- DD1: KSDS (プライマリ) のDD名。LRECL=1024バイトに設定
- OVFLW: ESDS (オーバーフロー) のDD名。LRECL=2048バイトに設定
DATASET DD1=CUSTKSDS,OVFLW=CUSTESDS,DEVICE=3390, X
RECORD=(1024,2048)
- [ルートセグメント: CUSTOMER]
- PARENT=0 はこれが最上位(ルート)であることを示す。
- BYTES=150 はセグメントの物理長。
SEGM NAME=CUSTOMER,PARENT=0,BYTES=150
FIELD NAME=(CUSTID,SEQ,U),BYTES=10,START=1,TYPE=C
FIELD NAME=CUSTNAME,BYTES=40,START=11,TYPE=C
- [第2階層セグメント: ORDER (CUSTOMERの子)]
- BYTES=200。
SEGM NAME=ORDER,PARENT=CUSTOMER,BYTES=200
FIELD NAME=(ORDID,SEQ,U),BYTES=12,START=1,TYPE=C
FIELD NAME=ORDDATE,BYTES=8,START=13,TYPE=C
- [第3階層セグメント: ITEM (ORDERの子)]
- BYTES=100。
SEGM NAME=ITEM,PARENT=ORDER,BYTES=100
FIELD NAME=(ITEMSEQ,SEQ,U),BYTES=4,START=1,TYPE=P
FIELD NAME=ITEMNAME,BYTES=30,START=5,TYPE=C
DBDGEN
FINISH
END
チーフアーキテクトによるコード解説
- `ACCESS=HISAM`: このデータベースがHISAMアクセス手法を用いることを宣言している。これにより、DBMSはKSDSとESDSのデュアル・データセット構造を前提として動作する。
- `RECORD=(1024,2048)`: ここが極めて重要なチューニングパラメータだ。第一引数の`1024`はKSDSのLRECL(論理レコード長)、第二引数の`2048`はESDSのLRECLを示す。
- KSDSのLRECL(1024バイト)には、ルートセグメント(150バイト)と、できるだけ多くの直系の従属セグメント(ORDER=200バイト、ITEM=100バイト)を詰め込みたい。
- もし、平均的な顧客が1つの注文と3つの明細を持つなら、必要な容量は `150 + 200 + (100 3) = 650バイト` となり、1024バイト以内に完全に収まる。この場合、ESDSへのI/Oはゼロ、1回のKSDSリードだけで全データがメモリに乗る。
- `FIELD NAME=(CUSTID,SEQ,U)`: `SEQ`はこれがシーケンシャル・キー(整理キー)であることを示し、`U`(Unique)は重複を許さないことを意味する。HISAMのルートセグメントは必ず一意のキーを持たねばならない。
—
3. HISAMパフォーマンストラジディ:アンチパターンと崩壊のメカニズム
HISAMは美しく設計されれば超高速だが、データの特性変化に対して極めて脆い。設計レビューで私が絶対に却下する「HISAM崩壊パターン」を紹介する。
アンチパターン1:従属セグメントの「大爆発」とESDSロングチェーン
ある顧客が突然、数千件の注文(ORDER)と明細(ITEM)を作成したとする。
KSDSのLRECL(1024バイト)などは一瞬で突き破り、データはESDSへ溢れ出す。
KSDS: [Root] -> ESDS: [Dep1] -> ESDS: [Dep2] -> … -> ESDS: [Dep1000]
この状態になると、この顧客のデータをスキャンするために、DBMSはESDS上のポインタを1000回追いかけてディスクI/Oを繰り返すことになる。RDBのN+1問題など生易しく思えるほどの、壊滅的な「ポインタ・チェーニング・ヘル(Pointer Chaining Hell)」が発生する。
アンチパターン2:頻繁な「挿入(Insert)」と「削除(Delete)」による物理的腐敗
HISAMは、セグメントの追加や削除に対して「物理的な再配置」を行わない。
- 挿入(Insert): 新しい従属セグメントが追加されると、階層順序を維持するために、既存のセグメントを物理的に後ろに押し出すか、ESDSの空き領域にポインタでねじ込む。これにより、論理的な並び順と物理的な並び順が完全に乖離し、順次走査性能が著しく低下する。
- 削除(Delete): HISAMにおけるセグメント削除は、物理削除ではなく「削除フラグ(Delete Flag)」の書き込みに過ぎない。データセット内のスペースは解放されず、順次スキャン時にもそのデッドスペースを律儀に読み飛ばすオーバーヘッドが発生する。
解決策:再編成(Reorganization)の運用設計
HISAMを採用する場合、夜間バッチ等での「REORG(再編成)」の設計が必須不可欠だ。
REORGユーティリティを実行することで、データベースは一度シーケンシャルにアンロードされ、削除フラグがクリアされ、物理的に美しくソートされた状態でKSDS/ESDSへ再ロードされる。この運用を設計に組み込んでいないプロジェクトは、例外なく数ヶ月でパフォーマンス不全に陥る。
—
4. プロフェッショナルが取るべき「極限の設計ガイドライン」
HISAMの限界を理解した上で、これを「牙を抜いた猛獣」として完璧に手懐けるための設計原則を提示する。
指針1:LRECLの「黄金比」を算出せよ
KSDSのLRECLは大きすぎればディスクスペースとバッファメモリを無駄にし、小さすぎればESDSへのオーバーフローを招く。
- 計算式:
$$\text{Target LRECL} = \text{Size of Root} + \sum (\text{Average Occurrence of Dep}_i \times \text{Size of Dep}_i)$$
- この計算値の「80%〜90%のデータが1つのKSDSレコードに収まるサイズ」をKSDS LRECLの決定値とし、極端な例外(10%のヘビーユーザー)のみをESDSに逃がす設計にする。これが最もスペース効率とI/O効率のバランスが良い。
指針2:HISAM vs HDAM の境界線を引け
設計レビューにおいて、本当にHISAMであるべきか、HDAM(Hierarchical Direct Access Method)に変更すべきかを常に問い直せ。
- HISAMを選択すべきケース:
- ルートキーの範囲指定による「順次アクセス(Range Scan)」が頻繁に発生する。
- データの追加・削除が極めて少なく、参照が中心である。
- 階層が浅く、1ルートあたりの従属セグメント数が安定している。
- HDAMを選択すべきケース(HISAMを避けるべきケース):
- ランダムアクセスが100%であり、順次アクセスの必要が全くない(HDAMはハッシュアクセスを行うため、ルートへのアクセスがHISAMよりさらに速い)。
- 更新・挿入・削除が激しく発生する(HDAMはポインタによる直接接続のため、再編成の頻度を劇的に減らせる)。
—
5. まとめ:物理レイアウトを支配する者がシステムを支配する
HISAMは、決して過去の遺物ではない。
メモリサイズに限界があった時代に、いかにディスクI/Oの回数を極小化し、ミリ秒以下の応答速度を叩き出すかという、ハードウェアの限界に対する人類の知恵の結晶である。
現代のエンジニアリングにおいても、ファイルフォーマット(ParquetやORCなど)の内部構造や、NoSQLのワイドカラム設計において、この「HISAM的な物理局所性の設計」は形を変えて生き続けている。
「抽象化されたスキーマの裏で、物理ディスク上にビットがどう並んでいるか」
これを常に意識し、DBDの1パラメータにまで拘り抜くエンジニアであってほしい。君の設計するシステムが、極限の負荷に耐え抜くことを期待している。
コメント