はじめに:I/Oの局所性を支配する者が、階層型DBMSを制する
設計レビューで「パフォーマンスが出ないため、ハードウェアのI/Oスループットを上げたい」という相談を現場から受けるたび、私はまずDBD(Database Description)の空き領域(Free Space)設計を開かせます。
RDBMSのB+Treeインデックスであれば、ページスプリットが発生してもある程度バックグラウンド処理や透過的な領域拡張で誤魔化しが利くかもしれません。しかし、ポインタチェインによって親子・兄弟(Twin)関係を物理的に結合する階層型DBMS(代表格としてのIMS/DBなど)において、「挿入(Insert)時にターゲットブロックに空き領域がない」という事態は、パフォーマンスの死を意味します。
ブロック内に空きがなければ、レコード(セグメント)は遠く離れた別のブロックへと飛ばされ、ポインタを手繰るたびに物理I/Oが跳ね上がります。これが「フラグメンテーションによるI/O爆発」の正体です。
今回は、階層型DBMSのポインタ追従性能を極限まで引き出すための空き領域管理のアーキテクチャと、DBDレベルでの具体的な設計パターンを解説します。
—
1. 階層型DBMSにおける挿入処理とFree Spaceのメカニズム
階層型DBMS(HDAM: 階層直接アクセス法、HIDAM: 階層索引直接アクセス法など)におけるデータ配置の基本原則は、「関連する親子・兄弟セグメントを同一ブロック(または同一Control Interval: CI)内に物理的に密集させること」です。
【理想的な配置: 同一ブロック内】
[ Block 100 ] —————————————–+
| [Root: 顧客A] -> [Child: 注文1] -> [Child: 注文2] (Free) | <- 1回のI/Oで全走査可能
+------------------------------------------------------+
【Free Space枯渇による断片化】
[ Block 100 ] -------------------+
| [Root: 顧客A] -> (ポインタ) —-|—> [ Block 850 ] —————-+
+——————————–+ | [Child: 注文1] -> (ポインタ) –|—> [ Block 1240 ]
+——————————–+ | [Child: 注文2]
+—————-
※ 1つの階層を辿るだけで3回の物理I/Oが発生!
なぜFree Spaceが挿入速度を決定づけるのか?
1. ポインタ局所性(Locality of Reference)の維持
同一ブロック内にセグメントを挿入できれば、バッファプール内でポインタ解決が完結し、物理I/Oはゼロ(キャッシュヒット)または1回で済みます。
2. ビットマップ走査オーバーヘッドの抑止
ブロックが満杯の場合、DBMSは空き領域を管理する「スペース検索ビットマップ」を走査して代替ブロックを探します。この探索自体がCPUおよびI/Oコストを消費します。
3. RAP(Root Anchor Point)チェーンの短縮
HDAM等でハッシュ先ブロックにルートを直接格納できない場合、シノニムチェーンが物理的に離れたブロックへ伸び、参照性能が致命的に劣化します。
—
2. スキーマ定義(DBD)による空き領域の制御
階層型DBMSでは、データセット初期ロード時に「どれだけの頻度で、どれだけの空き領域を残すか」をDDL/DBDで宣言します。以下は標準的なDBDにおける`DATASET`マクロの空き領域パラメータ(`FRSPC`)の指定例です。
-asm
- 顧客・注文管理データベース (HIDAM構成例)
- – 挿入負荷の高い受注明細セグメントの局所性を維持するためのFree Space設計
DBD NAME=CUSTDBD,ACCESS=(HIDAM,VSAM)
- データセットグループ定義: 空き領域パラメータの指定
- fbff (Free Block Frequency Factor): 何ブロックごとに1ブロック丸ごと空けるか
- fspf (Free Space Percentage Factor): 各ブロック内に何%の空きを残すか
DATASET DD1=CUSTDAT,DEVICE=3390,SIZE=(4096),
FRSPC=(5,20)
- │ └─ 各ブロックに 20% の空き領域を確保
- └─── 5ブロック毎に 1ブロック(100%)を完全な空き領域とする
- ルートセグメント定義 (顧客マスター)
SEGM NAME=CUSTOMER,PARENT=0,BYTES=(120),PTR=TWIN
FIELD NAME=(CUSTID,SEQ,U),BYTES=10,START=1,TYPE=C
- 子セグメント定義 (注文トランザクション: 頻繁に追加挿入される)
SEGM NAME=ORDHIST,PARENT=CUSTOMER,BYTES=(80),PTR=TWINBWD
FIELD NAME=(ORDDATE,SEQ,M),BYTES=8,START=1,TYPE=C
DBDGEN
FINISH
END
パラメータの正確な理解:`FRSPC=(fbff, fspf)`
- `fspf` (Free Space Percentage Factor: 各ブロック内の空き率)
- 初期ロード(Initial Load)または再編成(Reorg)時、各ブロック内に指定した割合(%)の空きを強制的に残します。
- 適用場面: 既存の親セグメントの直下に、後から均等に子セグメントが追加されるパターン。
- `fbff` (Free Block Frequency Factor: 完全空きブロックの頻度)
- 指定したブロック間隔ごとに、丸ごと1ブロック(100%)をデータ書き込みなしの「完全空きブロック」として予約します。
- 適用場面: 特定のルート配下に大量の子セグメントが一括追加され、1ブロックのパーセンテージ枠(`fspf`)では到底収まらない巨大な塊が挿入されるパターン。
—
3. 実戦的アーキテクチャパターン:ワークロード別の設計
Free Space設計に「銀の弾丸」はありません。データのカーディナリティとライフサイクルに合わせた使い分けが求められます。
パターンA:均等追加型(例:日次で各顧客が数件のログを記録)
→ [ fspf=20〜30%, fbff=0 ]
→ 各ブロックに満遍なくスペースを散らし、親子同一ブロック配置を狙う。
パターンB:特定キー集中・バッチ一括型(例:一部の顧客に数千件の明細が一括挿入)
→ [ fspf=10%, fbff=2〜4 ]
→ ブロック内は適度にしつつ、溢れたセグメントを直近の「完全空きブロック」に
逃がすことで、ディスクヘッドのシーク距離を最短化する。
パターンC:リードオンリー・洗替型(例:マスタ系データベース)
→ [ fspf=0, fbff=0 ]
→ 挿入が発生しないため空き領域は無駄。密度を100%にしてバッファ効率を最大化する。
—
4. テクニカルリードが指摘すべき「3つの落とし穴」
レビュー時に以下のアンチパターンを発見した場合は、即座に設計を差し戻してください。
① 過剰なFree Spaceによる「バッファプール汚染」
「挿入が速くなるから」と安易に `fspf=50` や `fbff=2` などの過剰な設定をしてはいけません。
空き領域もメモリ(バッファプール)上にロードされます。つまり、バッファプールの実効容量が半分に激減し、参照処理(Get Unique / Get Next)のキャッシュヒット率が著しく低下します。更新性能のために参照性能を殺しては本末転倒です。
② セグメント長(BYTES)とブロックサイズの不整合
ブロックサイズが4096バイト、セグメント長が1500バイトの設計で `fspf=20`(約800バイトの空き)を設定しても、1500バイトのセグメントは追加挿入できません。「残した空き領域の絶対バイト数 < 挿入セグメントのバイト数」になっていないか、必ず計算を確認してください。
③ Reorg(再編成)戦略なきFree Space設計
Free Spaceは「時間の経過とともに必ず使い果たされる」資源です。
- 空き領域が枯渇した段階で、挿入パフォーマンスは崖を落ちるように急激に悪化します。
- 対策: 空き領域の消費進捗(断片化率、非同一ブロックへのチェーン比率)を監視ツールでモニタリングし、閾値を超えたら自動で Unload/Reload(再編成)を走らせる運用設計を最初からセットで組み込むことが必須です。
—
まとめ:ミリ秒以下の世界を支配する設計を
階層型DBMSのFree Space設計とは、ディスク上の物理ブロックというミクロな世界において、「将来発生するI/Oの発生位置を事前にコントロールする」高度なアーキテクチャワークです。
1. アクセスパターン(均等か局所か)から `fspf` と `fbff` をロジカルに割り出す
2. セグメントサイズとブロックサイズの境界値を厳密に計算する
3. バッファプール効率とのトレードオフを意識し、Reorg運用まで見届ける
この3原則を徹底し、極限まで無駄のない、堅牢で超高速なデータ基盤を構築してください。
コメント