VSAMの息継ぎを整える:PL/I `ENVIRONMENT`属性の`BUFND`/`BUFNI`チューニングと移行の罠
メインフレームの現場で夜間バッチのウィンドウがじりじりと狭まり、「おい、このままだとオンライン開始の06:00に間に合わないぞ」というピリついた空気が流れる。そんな修羅場で原因の多くを占めるのは、決まって大容量VSAM(KSDS)を伴う一括処理のI/Oボトルネックだ。
PL/I(Programming Language One)のコードを覗いてみると、ファイル定義の`ENVIRONMENT`属性が初期値のまま放置されているケースが実に多い。今回は、VSAMのパフォーマンスを極限まで引き出すための`BUFND`(データバッファ)および`BUFNI`(インデックスバッファ)の指定手法について、コンパイラの挙動、さらにはモダン言語へのマイグレーション時の隠れた地雷を踏まえないための実践知を共有しよう。
—
1. なぜVSAMのバッファ数がバッチの生死を分けるのか
VSAM KSDS(Key-Sequenced Data Set)に対する順次処理(SEQアクセス)やランダム処理(SKED/DIRECTアクセス)において、OS(z/OS)のアクセスメソッドサービス(VSAM管理モジュール)は、メモリ上に確保されたバッファプールを介してDASD(外付けディスク)とやり取りを行う。
もしバッファ数がデフォルト(通常はデータ2個、インデックス1個という悲惨な少なさ)のままであればどうなるか。
レコードを1件読み込むたびに物理的なI/Oが発生し、チャネルビジーやデバイス待ち(EXCP数の高騰)を引き起こす。特に数百万件を誇る基幹の勘定系マスタや契約データに対して、不適切なバッファサイズのまま大量のランダム参照を行うプログラムを走らせれば、CPUは遊んでいるのにプロセッサ時間の大半がI/O待ちで溶けていくという、メインフレーム運用者にとって悪夢のような状態に陥る。
ここで登場するのが、PL/Iのファイル宣言における`ENVIRONMENT`オプション、すなわち`BUFND`と`BUFNI`だ。
—
2. 実践:PL/Iコードにおける `BUFND` / `BUFNI` の指定と動的制御
まずは、実際のPL/Iソースコードにおける静的な定義を見てみよう。大文字で記述された厳格な構文の中に、チューニングの要が隠されている。
//
/ VSAM KSDS 顧客マスタ 高速バッチ処理プログラム /
//
CUSTBAT: PROC OPTIONS(MAIN);
/ 外部ファイル(VSAM KSDS)の宣言 /
DCL CUST_FILE FILE RECORD
ENV(VSAM
BUFND(20) / データバッファを20個に拡張 /
BUFNI(10) / インデックスバッファを10個に拡張 /
);
/ 顧客レコードの構造体定義 /
DCL 1 CUST_REC,
5 CUST_ID PIC ‘9(08)’, / 顧客ID (キー項目) /
5 CUST_NAME CHAR(40), / 顧客名 /
5 CUST_DATA CHAR(452); / その他属性 /
DCL EOF_FLG CHAR(1) INIT(‘OFF’);
/ ファイルのオープン(UPDATE/INPUT) /
OPEN FILE(CUST_FILE) INPUT;
/ 終了電文のハンドリング /
ON ENDFILE(CUST_FILE) EOF_FLG = ‘ON’;
DO WHILE(EOF_FLG = ‘OFF’);
READ FILE(CUST_FILE) INTO(CUST_REC);
if EOF_FLG = ‘ON’ then leave;
/ ここにビジネスロジック(高速なキー処理など)を記述 /
CALL PROCESS_RECORD(CUST_REC);
END;
CLOSE FILE(CUST_FILE);
RETURN;
PROCESS_RECORD: PROC(P_REC);
DCL 1 P_REC,
5 CUST_ID PIC ‘9(08)’,
5 CUST_NAME CHAR(40),
5 CUST_DATA CHAR(452);
/ 個別処理のシミュレーション /
NULL;
END PROCESS_RECORD;
END CUSTBAT;
パラメータ選定のアーキテクチャ的指針
- `BUFND`(Buffer Number for Data): 順次処理が主体の場合は多めに取ることで、シリンダ単位の先読み(Read-ahead)の効果を最大化できる。一般的には多重度やデータシリンダの大きさに応じて `10` から `50` 程度を指定する。
- `BUFNI`(Buffer Number for Index): KSDSのB-Tree構造におけるインデックスセットブロックを常駐させるためのもの。ルートノードや上位シーケンスセットがメモリ上にヒット(インデックスヒット率100%)すれば、DASDへのインデックスアクセスをほぼゼロに抑え込める。こちらは `5` から `15` 程度が妥当なことが多い。過剰に大きくしてもメモリを圧迫するだけで費用対効果が薄れる。
—
3. アーキテクチャの裏側:コンパイラ最適化とAMODE/RMODEの罠
この`ENVIRONMENT(BUFND/BUFNI)`を指定した際、IBM Enterprise PL/Iコンパイラは内部でどのように動いているのか。
コンパイラは生成するオブジェクトコード内にACB(Access Method Control Block)およびRPL(Request Parameter List)の生成命令を埋め込む。ここで注意すべきは、バッファ数を増やすということは、プログラムが動作するタスクの仮想記憶(ストレージ)消費量が増加するという事実だ。
24ビットアドレッシング(AMODE 24)の呪縛
もし古いレガシープログラムでAMODE 24の制約を引きずっている場合、拡張バッファ群は16MB境界(LPA/SQA/CSA、あるいはタスクのローカルストレージ)より下の貴重な領域を食いつぶすことになる。
バッファを増やした途端に `S80A` や `S806` といったストレージ不足アベンド(ABEND)が発生するのは、この境界線を超えた領域枯渇が原因であることが多い。現代の移行プロジェクトにおいては、コンパイラオプションで必ず `AMODE(31)`(あるいは64ビット化)を指定し、バッファ領域を上方仮想記憶(16MB境界より上)へ追い払うことが大前提となる。
—
4. エッジケース:CICSオンラインおよびDB2併用時の「バッファ競合」
バッチ処理であれば上記の静的指定で事足りるが、これがCICSオンライン領域から起動されるPL/Iトランザクションや、DB2の埋め込みSQL(EXEC SQL)とVSAMアクセスが混在するプログラムになると話は別次元の複雑さを帯びる。
1. CICS環境下のFCT(File Control Table)定義との矛盾
CICS環境下では、ファイルのバッファ数はFCT(またはRDO定義)側で制御されるのが基本だ。PL/Iの`ENVIRONMENT`属性で下手に大きな`BUFND`を静的定義しても、CICSのファイル管理機構(File Manager)とコンフリクトを起こし、最悪の場合はスレッドセーフティを損なうか、無視される。オンラインプログラムでは、ファイル制御はCICSに委ね、PL/I側の`ENVIRONMENT`は最小限にするか環境依存の切り分けが必要になる。
2. パックデシマルの内部符号反転とDB2ホスト変数
VSAMのキーに `PIC ‘S9(8)’ COMP-3` などのパックデシマルを使用し、それをさらにDB2の検索キーに流し込むようなハイブリッドな設計の場合、バッファリングされたデータ領域をポインタ(`POINTER`)で直接キャストして高速操作しようとすると、ゾーン部の異常や符号(Sign)の反転バグを踏み抜くことがある。
特にマイグレーション時にJavaの `BigDecimal` や C#の `decimal` へロジックを移植する際、VSAM特有のパック表現のパディングとバッファアラインメントのズレが原因で、静かにサイレントデータ破損を引き起こすケースを数多く見てきた。ポインタベースでバッファを直叩きするアプローチは、極限のパフォーマンスが求められる箇所以外では封印すべきである。
—
5. レガシー移行(Java / C# / クラウド)における設計指針
「じゃあ、このPL/IとVSAMの絶妙なチューニングノウハウは、オープン系やクラウドへの移行(モダナイゼーション)においてどう活きるのか?」
ここがシニアアーキテクトの腕の見せ所だ。
Java(Spring Bootなど)やC# (.NET Core)へのリライト、あるいはRDB(PostgreSQLやOracle、あるいはCloud Spanner)へのデータストア移行を行う際、単に「VSAMのKSDSをRDBのテーブルに置き換えました」では、元のメインフレームが持っていたI/Oスループットの妙味を完全に破壊してしまう。
- インデックス設計の模倣:
VSAMの`BUFNI`が担っていた「インデックスのメモリ常駐効果」は、移行先のRDBにおいて十分なサイズを持つ共有プール(Buffer Pool)の設計や、適切なB-Treeインデックスの貼り方、さらにはアプリケーション層でのEhcacheやRedisなどのインメモリキャッシュ戦略にそのまま読み替える必要がある。
- 一括処理(Batch)のパラレル化:
VSAMのシリンダ先読み(`BUFND`による効果)の代替として、オープン系のバッチでは「パーティショニング(分割処理)」や「マルチスレッドによる非同期フェッチ」を設計に組み込まなければ、移行完了後に「バッチ処理時間が3倍に膨れ上がった」というクレームを受けることになる。
—
結びにかえて
PL/Iの`ENVIRONMENT(BUFND/BUFNI)`という、一見すると地味な数文字のパラメータ。しかしそこには、ハードウェアの物理特性(DASDのシリンダ、セクタ、チャネル)を限界まで絞り尽くそうとした先人たちの執念と、コンパイラ・OS・アーキテクチャの深い理解が詰まっている。
コードを書き換えるときは、単に「動く」だけではなく、その背後でメモリとI/Oがどう呼吸しているのかを想像してほしい。その視点を持てるかどうかが、真のシステムアーキテクトと、単なるコード翻訳者の分かれ道である。
