【実務・中級編】 データセットグループ化 – 階層型DBMS

はじめに:なぜ単一データセット運用は破綻するのか

データベース設計の現場において、論理設計(ツリー構造の定義)に終始し、物理配置の最適化を後回しにしているケースをしばしば見かける。だが、階層型DBMS(IMS/DBなど)の真のパフォーマンスを引き出せるかどうかは、「物理データセット構造を論理ツリーとどう連動させるか」に懸かっている。

階層型DBMSの代表的なアクセス方式であるHDAM(Hierarchical Direct Access Method)やHIDAMにおいて、すべてのセグメントを1つの物理データセットに押し込めると、以下のような致命的な問題が発生する。

1. バッファプールの汚染: 低頻度でしかアクセスされない巨大な可変長セグメント(例: 履歴明細や自由記述テキスト)が、高頻度で参照されるルートセグメントやヘッダセグメントをバッファから追い出す。
2. ディスクI/Oの直列化: 複数トランザクションが同時に異なる階層のセグメントを読み書きする際、同一データセットに対するヘッドシークとブロック競合が発生し、I/O待ちが急増する。
3. Reorg(データベース再編成)の肥大化: 一部の高トランザクションセグメントのフラグメンテーション解消のために、データベース全体をアンロード・リロードしなければならなくなる。

これらを一撃で解決するアーキテクチャが、今回解説する「データセットグループ化(Data Set Groups: DSG)」だ。今回は、実務でミッションクリティカルシステムを支えるエンジニアに向けて、DSGの内部メカニズムからDBD(Database Description)の実装、そして実戦的な設計パターンまでをロジカルに解説する。

—

データセットグループ化(DSG)の物理メカニズム

データセットグループ化とは、1つの論理的なデータベース階層(ツリー構造)を構成するセグメント群を、複数の物理データセット(Primary DSG および 1つ以上の Secondary DSG)に分割して格納する技術である。

【論理構造 (ツリー)】
[ルートセグメント (CUSTOMER)] <-- 高頻度アクセス / \ [注文 (ORDER)] [履歴 (HISTORY)] <-- 低頻度・巨大サイズ | [明細 (ITEM)] 【DSGによる物理分割配置】 [Primary DSG (Dataset A)] [Secondary DSG (Dataset B)] +-----------------------+ +-------------------------+ | CUSTOMER (ルート) | ---> | HISTORY (履歴) |
| ORDER (注文) | | +————————-+
| ITEM (明細) | | ※独立したバッファプールで管理
+———————–+ | ※物理I/Oを完全に分離
+— (Direct Address Pointerによるリンク)

1. ポインタ管理とアドレス解決

DSGを分割した場合、異なるデータセット間にまたがるセグメント同士は、ダイレクト・アドレス・ポインタ(4バイトのRBA: Relative Byte Address)によって直接リンクされる。

ここで重要な制約がある。階層ポインタ(Hierarchical Pointer)はデータセットグループを跨ぐことができない。したがって、DSGを採用する場合は、必ず物理子(PC: Physical Child)/ 物理兄弟(PT: Physical Twin)ポインタなどのダイレクトポインタ方式を採用する必要がある。この設計を怠ると、DBDGENの段階で弾かれるか、予期せぬポインタ走査コストを払うことになる。

2. バッファプール(OSAM/VSAM)の完全分離

DSGの最大の利点は、データセットごとに独立したサブプール(Subpool)をアサインできる点にある。
Primary DSGには高速なトランザクション処理用の小ブロック・大容量バッファを割り当て、Secondary DSGには巨大セグメント用の大ブロック・専用バッファを割り当てる。これにより、メモリ空間の競合(キャッシュスラッシング)を物理レベルで根絶できる。

—

DBDによる実装例:単一DS vs マルチDSG

実際のDBD定義でその差を見てみよう。以下は、顧客(CUSTOMER)をルートとし、注文(ORDER)、明細(ITEM)、そして巨大な変更履歴(CUSTHIST)を持つデータベースの定義例だ。

改善前:単一データセットによるアンチパターン

すべてのセグメントがデフォルトのPrimary DSGに押し込まれている。

-asm
DBD NAME=CUSTDB,ACCESS=(HDAM,VSAM),RMNAME=(DFSHDC40,3,500,824)
DATASET DD1=CUSTDAT1,DEVICE=3390,BLOCK=4096

SEGM NAME=CUSTOMER,BYTES=150,PTR=TWIN
FIELD NAME=(CUSTID,SEQ,U),BYTES=10,START=1,TYPE=C

SEGM NAME=ORDER,PARENT=CUSTOMER,BYTES=80,PTR=TWIN
FIELD NAME=(ORDNO,SEQ,U),BYTES=12,START=1,TYPE=C

SEGM NAME=ITEM,PARENT=ORDER,BYTES=60,PTR=TWIN
FIELD NAME=(ITEMNO,SEQ,U),BYTES=8,START=1,TYPE=C

  • 問題箇所: 巨大かつアクセス頻度の低い履歴セグメントが同居している

SEGM NAME=CUSTHIST,PARENT=CUSTOMER,BYTES=2048,PTR=TWIN
FIELD NAME=(HISTDATE,SEQ,U),BYTES=8,START=1,TYPE=C
DBDGEN
FINISH
END

改善後:セカンダリデータセットグループ(DSG)の導入

`DATASET` マクロを複数定義し、セグメントの物理配置を論理的に切り離す。

-asm
DBD NAME=CUSTDB,ACCESS=(HDAM,VSAM),RMNAME=(DFSHDC40,3,500,824)

  • ====================================================================
  • Primary Data Set Group: オンラインでミリ秒応答が求められる高頻度データ
  • ====================================================================

DSG1 DATASET DD1=CUSTPRI,DEVICE=3390,BLOCK=4096

SEGM NAME=CUSTOMER,PARENT=0,BYTES=150,PTR=TWINBWD
FIELD NAME=(CUSTID,SEQ,U),BYTES=10,START=1,TYPE=C

SEGM NAME=ORDER,PARENT=CUSTOMER,BYTES=80,PTR=TWINBWD
FIELD NAME=(ORDNO,SEQ,U),BYTES=12,START=1,TYPE=C

SEGM NAME=ITEM,PARENT=ORDER,BYTES=60,PTR=TWIN
FIELD NAME=(ITEMNO,SEQ,U),BYTES=8,START=1,TYPE=C

  • ====================================================================
  • Secondary Data Set Group: バッチ参照メインの低頻度・巨大履歴データ
  • ====================================================================

DSG2 DATASET DD1=CUSTSEC,DEVICE=3390,BLOCK=8192

  • DATASETオペランド(またはDATASETマクロ直下の定義順)でDSG2にアサイン

SEGM NAME=CUSTHIST,PARENT=CUSTOMER,BYTES=2048,PTR=TWINBWD
FIELD NAME=(HISTDATE,SEQ,U),BYTES=8,START=1,TYPE=C

DBDGEN
FINISH
END

レビューのポイント:

  • `DSG2` では `BLOCK=8192` を採用。2KB超の `CUSTHIST` セグメントを格納する際のブロック内断片化(スラック領域)を最小化している。
  • `DSG1` は 4KB ブロックのままとし、オンライン参照時のページ転送オーバーヘッドを抑制している。
  • `PTR=TWINBWD`(双方向ツインポインタ)を採用し、削除・挿入時のポインタ更新に伴うデータセット跨ぎのI/Oコストを局所化している。

—

実務で勝つための3大設計パターン

DSGを効果的に適用するための明確な基準を持っておく必要がある。設計レビュー時には、以下の3つのパターンのいずれかに合致しているかを必ず確認してほしい。

パターン1:アクセス頻度(Hot/Cold)による分離

  • 適用基準: トランザクションの90%がツリーの上位セグメントのみを参照し、下位の一部のセグメントが月次バッチや監査ログ用途の場合。
  • 狙い: Hotデータ用のデータセットを小さく保ち、バッファヒット率を極限まで引き上げる(全ブロックのメモリ常駐化を狙う)。

パターン2:セグメント長(Size Discrepancy)の極端な差異の隔離

  • 適用基準: 平均長が50~100バイトのセグメント群の中に、1KBを超える大型可変長セグメントが混在している場合。
  • 狙い: ブロックサイズの違いによる内部フラグメンテーションの根絶。大型セグメント専用のDSGには大きなCI/ブロックサイズ(8KB/16KB)を、小型セグメントには4KBを割り当てる。

パターン3:走査局所性(Scan Locality)の最適化

  • 適用基準: 特定の業務トランザクションが、特定の親子関係セグメント(例: `CUSTOMER` から `ORDER` を経由せず `SPECIAL_AUDIT` セグメントのみ)を一括走査する場合。
  • 狙い: 不要な中間・兄弟セグメントをスキップし、ディスク上の物理シーケンシャルリードを最速化する。

—

パフォーマンスと運用上の注意点

DSGは万能薬ではない。誤った設計を行えば、かえってオーバーヘッドを増大させる。以下のトレードオフは必ず認識しておくべきだ。

【トレードオフの比較】
+——————-+———————————–+———————————–+
| 観点 | 単一データセット運用 | マルチDSG運用 |
+——————-+———————————–+———————————–+
| バッファ効率 | 低 (混在によりキャッシュ汚染) | 極めて高 (データセット単位で最適化)|
| I/O並行性 | 悪 (同一デバイス/データセット競合)| 良 (物理チャネル/ボリューム分散可)|
| ランダムI/Oコスト | 単一ブロック内で解決する可能性あり| DSG跨ぎ時に確実に追加I/Oが発生 |
| 運用管理コスト | JCL定義・バックアップが単純 | 複数DDの管理・リカバリ手順の複雑化|
+——————-+———————————–+———————————–+

1. DSG跨ぎアクセスの「局所性喪失」に注意せよ

単一データセットであれば、運が良ければ親セグメントと子セグメントが同一ブロック内に収まり、1回の物理I/Oで両方を取得できる(Hierarchical Clustering)。
しかし、DSGで物理分割した場合、親から子へのアクセスは100%異なるデータセット(別ブロック)へのI/Oになる。親子を常に「ワンセット」でしかアクセスしない関係であれば、それらを別々のDSGに分割してはならない。

2. ポインタ設計は「ダイレクトポインタ(PC/PT)」を死守する

前述のとおり、DSGを跨ぐ階層走査はポインタチェーンの断絶を意味する。DSGを採用するツリーでは、セグメントの挿入・削除に伴うI/Oを最小化するため、必ず物理親ポインタ(PP)や双方向ツインポインタ(TWINBWD)を適切に組み合わせて設計すること。

3. DFSVSAMP / DFSVSMxx によるバッファプール設計

DBDでDSGを分割しても、実行時のOSAM/VSAMバッファ定義(DFSVSAMP)で同一のサブプールを共有させてしまっては意味がない。
必ずデータセットのブロックサイズに応じた個別のサブプールを定義し、Primary DSGとSecondary DSGのバッファを物理メモリ上で完全に隔離すること。

—

まとめ:物理構造まで支配してこそのアーキテクトだ

データセットグループ化(DSG)は、単なるストレージの分割テクニックではない。「バッファキャッシュの保護」「I/O並行性の確保」「ストレージブロック効率の最大化」を同時に達成するための高度なアーキテクチャ戦略である。

  • 高頻度セグメントと巨大・低頻度セグメントを同一DSGに同居させない。
  • 親子セグメントの同時アクセス特性を見極め、安易な分離によるランダムI/O増加を避ける。
  • DBDの定義だけでなく、ポインタ構造、ブロックサイズ、DFSVSAMPバッファ設計までを一気通貫でロジカルに設計する。

論理ツリーの美しさに満足せず、ディスクの1ブロック、メモリの1バッファの挙動まで完全に制御下に置く設計を徹底してほしい。

コメント

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