【実務・中級編】 論理データベースのリカバリ – 階層型DBMS

論理データベースのリカバリ:ポインタの不整合が招く崩壊と、その極限の回避策

階層型DBMSの真骨頂は、その極限にまで最適化されたアクセススピードにある。しかし、異なる物理データベース(物理DB)間に「論理関係(Logical Relationship)」を定義した瞬間、我々は一種の悪魔の契約を結ぶことになる。

関係データベース(RDB)の外部キー制約であれば、仮に整合性が崩れてもSQLのクエリ結果が矛盾する、あるいは制約エラーが出る程度で済む。しかし、物理ポインタでレコード間を結合する階層型DBMSにおいては、わずか1ビットのポインタの不整合が、OSレベルのセグメンテーションフォールト、データ破壊、あるいは最悪の場合、DBエンジンそのもののクラッシュを引き起こす。

本稿では、論理関係を持つ階層型データベースにおいて、データ整合性を完全に保ったままバックアップおよびリカバリ(特にPoint-in-Time Recovery: PITR)を遂行するためのアーキテクチャと、実務における極限の設計パターンを解説する。

—

1. 核心のアーキテクチャ:なぜ「単体リカバリ」は禁忌なのか

階層型DBMS(例えばIBMのIMS/DBなど)における論理関係は、物理的に異なる木構造(データベース)同士を、セグメントのプレフィックス内に埋め込まれた「物理アドレス(RBA: Relative Byte Address)」または「論理キー(LPCK: Logical Parent Concatenated Key)」で直結することで実現される。

[物理DB: CUSTDB (顧客)] [物理DB: ORDDB (注文)]
+—————–+ +—————–+
| CUSTOMER (親) | <---------------- | ORD_ITEM (子) | (論理子セグメント) +-----------------+ [論理ポインタ] +-----------------+ ここで、あるバグやハードウェア障害により、`ORDDB`のみを「昨日のバックアップ」にロールバック(リストア)し、`CUSTDB`は「最新の状態」のままにしたとしよう。 何が起こるか?

  • ハングポインタの発生: `ORD_ITEM`が指し示す先のRBAに、`CUSTDB`側で既に別の顧客データが上書きされている、あるいはセグメント自体が削除されている場合、エンジンは不正なメモリ領域を読みに行く。
  • 逆方向リンクの破綻: 論理親子関係では、多くの場合、双方向ポインタ(Logical Child First / Logical Parent Lastなど)が張られる。片方だけを過去に戻せば、双方向ポインタのチェーンは一瞬で引きちぎられる。

したがって、「論理関係を持つデータベース群は、単一の『リカバリ・ドメイン(回復境界)』として扱わなければならない」。これが鉄則である。

—

2. スキーマ定義(DBD)の実例:論理リンクの可視化

まずは、議論の前提となるスキーマ定義(DBD: Database Description)を見ておこう。ここでは、顧客(CUSTOMER)と注文(ORDER)を論理的に結合する。

物理DBD 1: 顧客データベース (`CUSTDB`)

-asm

  • 顧客セグメントを定義。注文DB側からの論理的アクセスを受け入れる。

DBD NAME=CUSTDB,ACCESS=HDAM,RMNAME=(DFSHDC40,1,500,824)
DATASET DD1=CUSTDAT,DEVICE=3390

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

  • 注文DBのORD_ITEMから「論理親」として参照されるターゲット

LCHILD NAME=(ORDITEM,ORDDB),PAIR=CUSTLNK,INDEX=CUSTIDX

SEGM NAME=CUSTLNK,PARENT=CUSTOMER,POINTER=PAIRED, X
SOURCE=((ORDITEM,KEY,ORDDB))
FIELD NAME=(ORDID,SEQ,U),BYTES=10,START=1,TYPE=C

DBDGEN
FINISH
END

物理DBD 2: 注文データベース (`ORDDB`)

-asm

  • 注文セグメントと、顧客DBへの論理リンクを持つ論理子セグメント

DBD NAME=ORDDB,ACCESS=HIDAM
DATASET DD1=ORDDAT,DEVICE=3390

SEGM NAME=ORDER,BYTES=100,PARENT=0
FIELD NAME=(ORDID,SEQ,U),BYTES=10,START=1,TYPE=C

  • 論理子(Logical Child)セグメントの定義
  • PHYSICAL PARENTはORDER、LOGICAL PARENTはCUSTDBのCUSTOMER

SEGM NAME=ORDITEM,PARENT=((ORDER,DBLE),(CUSTOMER,P,CUSTDB)), X
BYTES=80,POINTER=(LPARNT,DBLE)
FIELD NAME=(ITEMID,SEQ,U),BYTES=10,START=1,TYPE=C

DBDGEN
FINISH
END

この定義により、`ORDITEM`セグメントは物理的には`ORDDB`に属しながら、論理的には`CUSTDB`の`CUSTOMER`を親として指し示す。この「見えない糸(ポインタ)」が、リカバリを極限まで難しくする。

—

3. 整合性を保つリカバリ:3つのアプローチ

論理関係を持つ複数のデータベースをリカバリする場合、以下の3つのアプローチから、システムの許容ダウンタイムと運用コストに応じて選択する。

| アプローチ | 概要 | メリット | デメリット |
| :— | :— | :— | :— |
| 1. 同期リカバリ・グループ
(Coordinated PITR) | 関連する全DBを、ログ(OLDS/SLDS)を用いて全く同一のタイムスタンプ/LSNまでロールフォワード/バックする。 | ポインタの再構築処理が不要。最もクリーン。 | 全DBを同時にオフラインにする必要があり、ログ管理が極めて厳密。 |
| 2. プレフィックス再構築
(Prefix Resolution / Update) | 物理DBを個別に(異なる時点から)リストアし、ユーティリティを用いてポインタ関係のみをバッチで再生成・修正する。 | 各DBのリカバリタイミングに柔軟性がある。 | 大規模DBではポインタのソートと再書き込みに膨大なI/Oと時間がかかる。 |
| 3. 論理関係の一時解除
(Logical Detach & Re-attach) | スキーマ定義を一時的に論理関係のない構成に変更して個別リカバリし、後からアプリケーション側で整合性を担保する。 | 特定DBのみの超高速復旧が可能。 | アプリケーション側に整合性担保のロジックが必要になり、複雑化する。 |

実務における「王道」かつ「最も強固」な手法は、アプローチ1(同期リカバリ)であり、これが不可能な場合の代替案がアプローチ2(プレフィックス再構築)である。

—

4. 極限の実践:同期リカバリ・グループ(Coordinated PITR)

同期リカバリを成功させるためのステップを、タイムラインに沿って解説する。

[正常稼働] —> [障害発生] —> [1. 静止点の特定] —> [2. 全DBオフライン] —> [3. ログ適用] —> [4. 整合性検証]

ステップ 1: 静止点(Quiet Point / CHKP)の特定

ログ(SLDS/OLDS)を解析し、関連するすべての物理DB(`CUSTDB` と `ORDDB`)に対する更新トランザクションが完全にゼロであった「静止点(リカバリ・トークン)」を特定する。
> 警告: トランザクションが実行中(インフライト状態)の時点にリカバリすると、論理ポインタの生成が途中の状態になり、即座にポインタ不整合が発生する。

ステップ 2: データベースのオフライン化

リカバリ対象のすべてのDBをオフラインに設定し、アプリケーションからのアクセスを遮断する。
-asm
/DBR DB CUSTDB
/DBR DB ORDDB

ステップ 3: ログ復元ユーティリティの実行

データベース・リカバリ・ユーティリティ(DFSRRC00)を使用し、特定した静止点(例えば、ログの特定ブロック番号やタイムスタンプ)を指定して、すべてのDBを同時に復元する。

以下は、同期リカバリを実行するJCL(Job Control Language)の概念例である。

//RCVJOB JOB (ACCT),’DB RECOVERY’,CLASS=A,MSGCLASS=X
//——————————————————————-
// 1. CUSTDB のリストアおよびイメージコピーの適用
//——————————————————————-
//RCVCUST EXEC PGM=DFSRRC00,PARM=’UDR,DFSURDB0,CUSTDB’
//STEPLIB DD DISP=SHR,DSN=IMS.SDFSRESL
//DFSUDMP DD DISP=SHR,DSN=IMS.DB.CUSTDB.IMGCOPY(0) 最新のイメージコピー
//DFSULOG DD DISP=SHR,DSN=IMS.LOG.SLDS.G1024V00 静止点までのシステムログ
//DFSVSAMP DD DISP=SHR,DSN=IMS.PROCLIB(DFSVSM10)
//SYSIN DD
RECOVER CUSTDB TO_TIMESTAMP=2023.305.12.00.00.000000
/
//——————————————————————-
// 2. ORDDB のリストア(CUSTDBと完全に同一のタイムスタンプを指定)
//——————————————————————-
//RCVORD EXEC PGM=DFSRRC00,PARM=’UDR,DFSURDB0,ORDDB’
//STEPLIB DD DISP=SHR,DSN=IMS.SDFSRESL
//DFSUDMP DD DISP=SHR,DSN=IMS.DB.ORDDB.IMGCOPY(0) 同一世代のイメージコピー
//DFSULOG DD DISP=SHR,DSN=IMS.LOG.SLDS.G1024V00 同一のログファイル
//DFSVSAMP DD DISP=SHR,DSN=IMS.PROCLIB(DFSVSM10)
//SYSIN DD
RECOVER ORDDB TO_TIMESTAMP=2023.305.12.00.00.000000
/

—

5. 最後の砦:プレフィックス再構築(Prefix Resolution)

もし、同期リカバリに失敗した、あるいは特定のDBだけを異なる時点から復元せざるを得ない場合、ポインタは完全に崩壊している。この状態でDBをオープンすれば、即座にシステム障害に発展する。

この時、我々を救う最後の砦が「プレフィックス解決ユーティリティ・チェーン」である。

階層型DBMSは、物理的なデータ位置の不整合を解消するため、以下の3つのバッチプロセスを高速に走らせ、ポインタを「力まかせに再構築」する。

[1. Database Scan (DFSURGS0)]
│
▼ (ワークファイル: DFSURWF1)
[2. Prefix Resolution (DFSURG10)] <--- ここで巨大なソート処理が発生 │ ▼ (ワークファイル: DFSURWF3) [3. Prefix Update (DFSURGP0)] 1. Database Scan (DFSURGS0):
復元した物理DB群を全スキャンし、論理親子関係に関わるすべてのセグメントの現在地(RBA)とターゲットキーを抽出し、ワークファイル(`DFSURWF1`)に書き出す。
2. Prefix Resolution (DFSURG10):
`DFSURWF1` を入力とし、ポインタの「紐付け先」と「紐付け元」をマッチングするために、超高速ソートを実行する。この処理がリカバリ全体のパフォーマンスボトルネックとなる。結果は `DFSURWF3` に出力される。
3. Prefix Update (DFSURGP0):
`DFSURWF3` の情報に基づき、物理DBの各セグメントのプレフィックスに、正しい物理RBAポインタを直接書き戻していく。

プレフィックス解決フェーズのJCL構成例

//——————————————————————-
// DFSURG10 (Prefix Resolution) の実行
//——————————————————————-
//RESOLVE EXEC PGM=DFSRRC00,PARM=’ULS,DFSURG10′
//STEPLIB DD DISP=SHR,DSN=IMS.SDFSRESL
// スキャンフェーズで作成されたワークファイルをインプット
//DFSURWF1 DD DISP=SHR,DSN=IMS.WORK.DFSURWF1
// ソート用のワーク領域(パフォーマンス最大化のため、十分なサイズを確保)
//SORTWK01 DD UNIT=SYSDA,SPACE=(CYL,(250,250))
//SORTWK02 DD UNIT=SYSDA,SPACE=(CYL,(250,250))
// 更新フェーズに引き渡すための指示書ファイル
//DFSURWF3 DD DSN=IMS.WORK.DFSURWF3,DISP=(NEW,CATLG),
// UNIT=SYSDA,SPACE=(CYL,(100,100)),
// DCB=(RECFM=VB,LRECL=1024,BLKSIZE=20480)
//SYSPRINT DD SYSOUT=
//SYSUDUMP DD SYSOUT=

—

6. パフォーマンス上の注意点と極限の設計プラクティス

これら論理リカバリを実務で設計・運用する際、テクニカルリードが絶対に死守すべき設計基準を示す。

① ソートワーク(SORTWK)の極大化とメモリチューニング

`DFSURG10`(Prefix Resolution)は、関係性の数に比例した莫大なソート処理を行う。もしここでI/Oボトルネックが発生すると、リカバリ時間が数時間単位で延伸し、SLA(サービス品質保証)を突破する。

  • 対策: DFSORT等のソートユーティリティに割り当てるメモリ(`SIZE=MAX`)を最大化し、極力ディスクソートを避けてメモリ内で処理を完結させること。

② 双方向ポインタ(Bidirectional Pointer)の乱用禁止

論理関係を定義する際、親から子、子から親への双方向リンク(Physical Parent / Logical Parent / Logical Child)は便利だが、これらを増やすほど、リカバリ時のスキャン時間とソートワークサイズは指数関数的に増加する。

  • 設計基準: 一方向ポインタ(単一方向のLPARNTポインタなど)で要件を満たせないか徹底的にレビューせよ。不要な逆方向リンクは設計の敗北である。

③ 論理関係の「局所化(リカバリ・ドメインの分断)」

システム全体のデータベースを網の目のように論理関係で繋いではならない。すべてのDBが単一のリカバリ・ドメインに取り込まれ、一部の破損がシステム全体の全停止・全同期リカバリを引き起こす。

  • 設計基準: 業務的に強く結合しているサブシステム(例:顧客と契約)内でのみ論理関係を許容し、他サブシステム(例:配送)との間は、論理関係ではなくアプリケーションによる遅延メッセージングやAPI的な結合(階層型DBにおいては別トランザクションによるアクセス)を採用せよ。

—

7. 終わりに

階層型DBMSにおける「論理関係」は、当時のエンジニアがハードウェアの限界に挑み、リレーショナルな表現を最速のポインタアクセスで実現するために生み出した、執念の結晶である。

しかし、その高速性の裏には、厳密なポインタの整合性担保という重い十字架が存在する。
本稿で解説した同期リカバリのプロセス、およびPrefix Resolutionの仕組みを深く理解し、設計段階から「リカバリ・ドメインの極小化」を徹底すること。これこそが、数十年稼働し続けるミッションクリティカルシステムを支える、チーフアーキテクトの矜持である。

コメント

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