【実務・中級編】 論理データベースユーティリティ – 階層型DBMS

RDBのノリで階層型を触るな——「ポインタ」という生々しい現実

「リレーショナルデータベース(RDB)の外部キー結合と同じ感覚で、階層型DBMSの論理関係(Logical Relationship)を設計・運用してはならない」

これが、私が設計レビューで若手アーキテクトに最初に叩き込む鉄則だ。

RDBにおける表同士の結合は、論理的な値の比較(JOIN)によって実行時に解決される。しかし、階層型DBMS(その代表格であるIBM IMSなど)における論理関係は、そんな生易しいものではない。セグメント(RDBでいうレコード)のヘッダ領域に埋め込まれた「物理的なストレージアドレス(RBA: Relative Byte Address)」という、生々しいポインタの鎖(チェーン)によって直接緊結されているのだ。

【物理データベース A】 【物理データベース B】
+———————–+ +———————–+
| CUSTOMER (顧客) | | PRODUCT (商品) |
| – 物理セグメント | | – 物理セグメント |
+———————–+ +———————–+
| ^
| (物理親子) |
v | (論理親子ポインタ: LP)
+———————–+ |
| ORDER (注文) |———————–+
| – 論理子セグメント |
+———————–+

この「直接ポインタ接続」こそが、階層型DBMSにミリ秒未満の超極限レスポンスをもたらす源泉であると同時に、運用を極めてスリリングなものにしている元凶でもある。

データの追加、削除、そしてデータベースの再構築(Reorganization)によってセグメントの物理配置が変わった瞬間、これらのポインタは一瞬にして「迷子」になる。この壊れやすいポインタの生態系を維持・修復するための生命維持装置が、今回解説する「論理データベースユーティリティ」群だ。

本稿では、このユーティリティ群が裏で何を行っているのか、そしてデータ破損を防ぎつつ限界までパフォーマンスを引き出すためのアーキテクチャを徹底的に解剖する。

—

深淵のメカニズム:論理関係とプレフィックス(Prefix)の構造

論理データベースユーティリティを乗りこなすには、セグメントの物理構造を脳内に焼き付ける必要がある。
階層型DBMSのセグメントは、大別してPrefix(プレフィックス)とData(実データ)の2つの領域から構成されている。

+———————————————————————–+
| セグメント構造 |
+———————————————————————–+
| <--------- Prefix (制御領域) ---------> | <------- Data (データ領域) -------> |
| Seg Code | Delete Byte | Pointer Array | Field 1 | Field 2 | … |
+———————————————————————–+
|
+– LP (Logical Parent Pointer)
+– LCF (Logical Child First Pointer)
+– LCL (Logical Child Last Pointer)
+– PP (Physical Parent Pointer)

ユーティリティが操作するのは、このPrefix領域だ。ここに格納される主要なポインタの種類を整理しておこう。

  • LP (Logical Parent) ポインタ: 論理子セグメントから、別階層にある論理親セグメントの物理アドレス(RBA)を指す。
  • LCF / LCL (Logical Child First / Last) ポインタ: 論理親から、その配下にある最初と最後の論理子を指す。
  • PP (Physical Parent) ポインタ: 論理子が、自身が物理的に属する親を指す(逆方向のトレース用)。

なぜ、再構築(Reorg)でポインタが破綻するのか?

物理データベースA(上図のCUSTOMER)を再構築するために、一度データをすべてアンロードし、空き領域を詰めてリロードしたとしよう。このとき、CUSTOMERセグメントの物理的な格納アドレス(RBA)は完全に変わる。

しかし、物理データベースB(ORDER)の中に書き込まれている「LPポインタ」は、リロード前の古いアドレスを指したままだ。この状態でデータベースを起動すれば、アプリケーションは全く無関係のゴミデータを参照するか、ポインタエラー(ハング、あるいはアベンド)を起こして即死する。

だからこそ、データベースの再配置時には、ポインタを完全に「解決(Resolution)」し直す一連のバッチプロセスが不可欠なのだ。

—

極限の運用ツール群:論理データベースユーティリティの正体

IMSなどの標準的な階層型DBMSでは、このポインタ再構築を、以下の「3つのユーティリティ」を連携させたパイプライン処理で行う。これが論理データベースユーティリティ(Database Resolution Utilities)の実体だ。

+———————————————————–+
| 1. Prereorganization (DFSURG10) |
| – どのデータベースが再構築され、どの関係が影響を受けるかを定義 |
+———————————————————–+
|
v
+———————————————————–+
| 2. Prefix Resolution (DFSURG20) |
| – アンロード/リロード時に吐き出されたワークファイルをソート |
| – 新旧の物理アドレス(RBA)を突き合わせ、新しいポインタを生成 |
+———————————————————–+
|
v
+———————————————————–+
| 3. Prefix Update (DFSURGP0) |
| – 生成された新しいポインタ群を、実際の物理ファイルへ書き戻す |
+———————————————————–+

この処理の美しさは、「巨大なポインタ更新を、ランダムアクセスではなく、シーケンシャルソートとマージで処理する」という点にある。もしこれを1件ずつランダムアクセスで書き換えていたら、数億件のデータを持つ大規模システムでは、ディスクI/Oのボトルネックによってバッチウィンドウが何日あっても足りなくなる。

—

実践:ポインタ破損の検知と復旧シナリオ(JCL・制御カード例)

では、実際のシステム管理で使われるJCL(Job Control Language)と制御カードの設計例を見ていこう。
ここでは、物理データベース `DB01`(再構築対象)と `DB02`(論理関係あり)の間で、ポインタを再構築する際の実践的なフローを示す。

STEP 1: 再構築事前準備 (Prereorganization – DFSURG10)

まず、どのデータベースのポインタを再構築するかを宣言し、システムにワークファイル(DFSWRK01等)のメタデータを生成させる。

//PREREORG JOB (ACCT),’DBA-TASK’,CLASS=A,MSGCLASS=X
// ===================================================================
// STEP 1: 論理関係の事前宣言とタスクリストの作成
// ===================================================================
//STEP10 EXEC PGM=DFSRRC00,PARM=’Ulu,DFSURG10′
//STEPLIB DD DSN=IMS.SDFSRESL,DISP=SHR
//DFSRESLB DD DSN=IMS.SDFSRESL,DISP=SHR
//IMS DD DSN=IMS.DBDLIB,DISP=SHR / DBD(スキーマ定義)ライブラリ /
//SYSIN DD
DBR=DB01 / 再構築を行うデータベース1 /
DBR=DB02 / 影響を受けるデータベース2 /
/
//DFSVSAMP DD
VSRBF=1024,4 / バッファプールの定義 /
/
//SYSPRINT DD SYSOUT=

STEP 2: ポインタ解決 (Prefix Resolution – DFSURG20)

データベースのアンロードおよびリロード(このステップは通常、高速アンロード/リロードツール等で行う)によって作成されたワークファイル(`DFSURWF1`)を入力とし、新しいポインタを算出する。
このステップでは、内部的に大規模なソート(OSのSORTパッケージなど)が走り、新しいRBAが古いRBAと突合されてマージされる。

//RESOLVE JOB (ACCT),’DBA-TASK’,CLASS=A,MSGCLASS=X
// ===================================================================
// STEP 2: DFSURWF1(リロード出力)を入力とし、ポインタを解決する
// ===================================================================
//STEP20 EXEC PGM=DFSRRC00,PARM=’Ulu,DFSURG20′
//STEPLIB DD DSN=IMS.SDFSRESL,DISP=SHR
//DFSRESLB DD DSN=IMS.SDFSRESL,DISP=SHR
//IMS DD DSN=IMS.DBDLIB,DISP=SHR
//
// [入力] リロードユーティリティやインデックス作成ツールから出力されたワークファイル
//DFSURWF1 DD DSN=IMS.WORK.WF1,DISP=SHR
//
// [出力] ポインタ更新用のワークファイル(Prefix Updateの入力となる)
//DFSURWF3 DD DSN=IMS.WORK.WF3,DISP=(NEW,CATLG,DELETE),
// UNIT=SYSDA,SPACE=(CYL,(500,100)),DCB=RECFM=VB
//
// [作業用] ソート用のワークファイル
//DFSURCDS DD DSN=IMS.CONTROL.DATASET,DISP=OLD
//SYSIN DD

  • RESOLVE CARD IS IMPLIED (デフォルトの解決処理を実行)

/
//SYSPRINT DD SYSOUT=

STEP 3: ポインタ書き戻し (Prefix Update – DFSURGP0)

最後に、`DFSURWF3` に格納された「解決済みポインタ」を、実際の物理データベースファイルに対してシーケンシャルに書き戻す。これにより、論理的な結合チェーンが完全に修復される。

//UPDATE JOB (ACCT),’DBA-TASK’,CLASS=A,MSGCLASS=X
// ===================================================================
// STEP 3: 解決されたポインタ情報を物理データベースに反映
// ===================================================================
//STEP30 EXEC PGM=DFSRRC00,PARM=’Ulu,DFSURGP0′
//STEPLIB DD DSN=IMS.SDFSRESL,DISP=SHR
//DFSRESLB DD DSN=IMS.SDFSRESL,DISP=SHR
//IMS DD DSN=IMS.DBDLIB,DISP=SHR
//
// [入力] STEP 2 で解決されたポインタ情報
//DFSURWF3 DD DSN=IMS.WORK.WF3,DISP=SHR
//
// [更新対象] 実際のデータベースデータセット
//DB01DAT DD DSN=IMS.DB01.DATA,DISP=OLD
//DB02DAT DD DSN=IMS.DB02.DATA,DISP=OLD
//
//SYSPRINT DD SYSOUT=

—

チーフアーキテクトが叩き込む「3つの鉄則」

論理データベースユーティリティの動作原理を理解した上で、実務の設計・運用で致命的な障害を起こさないための設計パターンを伝授する。

1. 論理関係は「最小限」に抑えよ(双方向ポインタの誘惑を断ち切る)

双方向論理関係(Physical Parent と Logical Parent の両方向から双方向にポインタを張る設計)は、一見便利に見える。しかし、これはポインタの更新コストと破損リスクを2倍にする。
アクセスパスを徹底的にプロファイリングし、「一方向(Uni-directional)ポインタ」+「副インデックス(Secondary Index)」で代替できないかを極限まで突き詰めよ。システムが10年、20年と生き残るための秘訣は、常に「スキーマのシンプルさ」にある。

2. 再構築(Reorg)のバッチウィンドウを「設計初期」から計算せよ

大規模な階層型データベースにおいて、Prefix Resolution(ポインタ解決)の実行時間は、ポインタの「総数」と「ソート性能」に完全に依存する。
物理設計段階で、論理子セグメントが数千万件規模に達することが予想される場合、深夜のバッチウィンドウ(メンテナンス時間)にこの処理が収まるかを必ずサイジングすること。
収まらない場合の対抗策は以下の通りだ。

  • データベースのパーティショニング: 影響範囲を特定のパーティションに限定し、Resolutionの対象範囲を絞り込む。
  • サードパーティ製高速ユーティリティの導入: BMCやIBMのHigh Performance Utility等、メモリ上でインライン処理を行う製品の導入を最初から予算に組み込む。

3. 整合性チェック(Pointer Checker)を自動化せよ

「ポインタの不整合」は、アプリケーションが異常終了するか、誤ったデータを読み込むまで表面化しない。これはサイレントデータコラプション(静かなるデータ破壊)を引き起こす。
本番運用では、週次または月次の定期メンテナンスジョブに、必ずPointer Checker(整合性検証ユーティリティ)を組み込むこと。

【Pointer Checker のチェック項目】
・LPポインタが指す先に、本当に対応する論理親が存在するか?
・LCF/LCLチェーンのポインタが途中でループしたり、NULLを指して途切れていないか?
・物理的なセグメントサイズと、Prefix内のポインタ長に矛盾がないか?

これらを事前に検知できれば、バックアップからの復旧や、ユーティリティによるポインタ再構築を「被害が出る前」に実行できる。

—

エピローグ:本物のエンジニアへ

RDBやNoSQLに慣れ親しんだ現代のエンジニアにとって、物理アドレス(ポインタ)を直接管理する階層型DBMSの運用は、まるで「素手で高圧電線を触る」ような恐怖を覚えるかもしれない。

しかし、この剥き出しのアーキテクチャこそが、現代の分散システムや仮想化レイヤーがどうしても超えられない「極限の低遅延」と「超高密度トランザクション」を半世紀にわたって支え続けている理由なのだ。

ユーティリティの挙動一つひとつ、JCLのパラメータ1行に込められた「物理構造への敬意」を忘れてはならない。低レイヤーを制する者こそが、真に堅牢なシステムを構築できる。

健闘を祈る。

コメント

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