こんにちは、皆さん。日々のメインフレーム保守やバッチウィンドウの圧迫に頭を悩ませていないかい?
「また夜間バッチの処理時間がSLA(サービス品質保証)に引っかかりそうだ」「KSDSのランダムアクセスがどうにもボトルネックになっている」――そんな悲鳴が、あちこちの運用チームから聞こえてくる季節だな。
今回は、PL/Iプログラマなら避けて通れない、しかし正しく理解すればバッチ劇的短縮の切り札となる「ENVIRONMENT属性のBUFND/BUFNIパラメータによるVSAMパフォーマンスチューニング」について、現場の泥臭い知見を交えて徹底的に解説しよう。
PL/Iには、COBOLの環境部(Environment Division)のような冗長な構文がない代わりに、DECLARE文の属性としてファイル制御のすべてを詰め込む。このシンプルさゆえに、デフォルトのままで本番稼働させてしまい、I/O待ち(EXCP回数の爆発)で沈没するシステムを数多く見てきた。
さあ、ベテランの知恵を授けよう。一緒にコードとサブシステムの内側を見ていこうか。
—
1. なぜVSAMのバッファチューニングが必要なのか?
VSAM(Virtual Storage Access Method)のKSDS(Key-Sequenced Data Set:鍵順データセット)を大量にランダム処理する夜間バッチを想像してほしい。
インデックス部(INDEX)とデータ部(DATA)のI/Oは、メインフレームのCPUパワーがどれだけ高くても、ディスク(DASD)へのアクセスが発生した瞬間に「待ち(Wait)」を生む。
デフォルトのままだと、システムが自動割り当てするバッファ数は最低限(データ1面、インデックス1面程度)だ。これでは、レコードを1件引くたびに物理的なDASDリードが発生し、チャネルビジーの嵐となる。
ここで登場するのが、PL/Iの `ENVIRONMENT` 属性における以下の2つのパラメータだ。
- BUFND (Buffer Number for Data): データバッファの面数。KSDSのデータ部分をメモリ上に保持する領域。
- BUFNI (Buffer Number for Index): インデックスバッファの面数。B-Tree構造のインデックス部をメモリ上に常駐させ、枝刈りをメモリ上で完結させるための領域。
これらを適切に増やすことで、「DASDへの物理I/Oをメモリ上の論理I/Oに置き換える(バッファヒット率を上げる)」ことが可能になる。これがチューニングの基本原理だ。
—
2. 予約語を持たないPL/Iの柔軟性と、ENVIRONMENT属性の罠
PL/Iの最大の特徴の一つは、「厳格な予約語(Reserved Words)を持たない」という点にある。`DECLARE` や `ENVIRONMENT` といったキーワードでさえも、文脈によってはただの変数名として使えてしまう(もちろん、そんな不毛な真似をするプログラマはいないがね)。
この柔軟性ゆえに、ファイル定義(DCL 〇〇 FILE …)の記述ミスはコンパイルエラーとして検出しにくい場合がある。特に `ENVIRONMENT` 属性の中に記述するパラメータ群は、JCL側の `ACB` や `DLBL`、あるいはVSAM定義時の `DEFINE CLUSTER` の設定とも連動するため、仕様を誤るとオープン時に致命的なエラー(abend)を誘発する。
これから示す実践的なコード例を見てほしい。大文字で統一された、厳格かつ美しいPL/Iのデータセット処理の基本形だ。
—
3. 実践!BUFND/BUFNIを指定したPL/Iプログラム例
以下のプログラムは、膨大なKSDSマスタファイルから特定の条件に合致するレコードをランダムに引き当て、更新または参照する典型的なバッチ処理の抜粋だ。
注目すべきは、`CUSTOMER_FILE` の `ENVIRONMENT` 属性における `BUFND(15), BUFNI(8)` の指定部分だ。
//
/ プログラム名: CUSTUPD1 – VSAM KSDS高速ランダム更新バッチ /
//
CUSTUPD: PROC OPTIONS(MAIN);
/ ————————————————– /
/ 1. 外部ファイル(VSAM KSDS)の定義 /
/ ————————————————– /
DCL CUSTOMER_FILE FILE RECORD
ENV(VSAM
BUFND(15) / データバッファを15面に拡張 (デフォルトより増強) /
BUFNI(8) / インデックスバッファを8面に拡張 /
);
/ ————————————————– /
/ 2. レコード構造体の定義 /
/ ————————————————– /
DCL 1 CUST_REC,
5 CUST_ID CHAR(8), / 顧客ID (キー項目) /
5 CUST_NAME CHAR(40), / 顧客名 /
5 CUST_STATUS CHAR(1), / ステータス /
5 FILLER CHAR(51); / 予備領域 /
DCL W_CUST_KEY CHAR(8);
DCL EOF_FLAG BIT(1);
DCL SQL_CODE FIXED BIN(31);
/ エラー制御(ONユニット)の宣言 /
ON ENDFILE(CUSTOMER_FILE) EOF_FLAG = ‘1’B;
ON UNDEFINEDFILE(CUSTOMER_FILE)
BEGIN;
PUT SKIP LIST(‘ 致命的エラー: 顧客ファイルが開けません。JCLを確認せよ。’);
SIGNAL ERROR;
END;
/ ————————————————– /
/ 3. 初期処理 /
/ ————————————————– /
EOF_FLAG = ‘0’B;
/ 更新モードでVSAMファイルをオープン /
OPEN FILE(CUSTOMER_FILE) UPDATE;
/ ————————————————– /
/ 4. メインループ(ランダムアクセス処理) /
/ ————————————————– /
/ ※ここでは説明のため、キーを指定して直接読み込む想定 /
W_CUST_KEY = ‘A1002345’;
READ FILE(CUSTOMER_FILE)
INTO(CUST_REC)
KEY(W_CUST_KEY);
IF DATAPTR(CUSTOMER_FILE) = NULL() THEN DO; / 実際にはKEYERROR等を捕捉 /
PUT SKIP LIST(‘対象レコードが存在しません。KEY=’ || W_CUST_KEY);
END;
ELSE DO;
PUT SKIP LIST(‘読込成功: ‘ || CUST_REC.CUST_NAME);
/ レコード内容の更新 /
CUST_REC.CUST_STATUS = ‘2’;
REWRITE FILE(CUSTOMER_FILE)
FROM(CUST_REC);
PUT SKIP LIST(‘レコードを更新しました。’);
END;
/ ————————————————– /
/ 5. 終了処理 /
/ ————————————————– /
CLOSE FILE(CUSTOMER_FILE);
PUT SKIP LIST(‘正常終了しました。’);
END CUSTUPD;
—
4. チューニングの勘所:いくつに設定すべきか?
「先生、じゃあBUFNDやBUFNIは大きければ大きいほど良いんですね? すべて99とかにすればいいですか?」
――こういう質問をよく後輩から受けるが、答えは「No」だ。
メインフレームの仮想ストレージ(31ビット、あるいは64ビットアドレス空間)も無限ではない。バッファを無闇に拡大すると、以下の弊害が生じる。
1. リージョンサイズの圧迫: アドレス空間(Region)が枯渇し、S806やS0C4などのシステムabendを引き起こす。
2. VSAMローカル・シリアライゼーションの競合: バッファプールが大きすぎると、逆に内部ラッチの競合コストが高くなり、マルチタスク環境でスループットが落ちることがある。
現場で使える経験則(チートシート)
- BUFNI (インデックスバッファ):
- 基本は「インデックスの階層数(Levels)の全ブロックがメモリに乗る数」+αが理想。
- 通常のKSDSであれば、`BUFNI(4)` から `BUFNI(8)` あたりの指定でインデックスの大部分が常駐し、ルートおよび中間ノードのI/Oがほぼゼロになる。これ以上増やしても効果が頭打ちになりやすい。
- BUFND (データバッファ):
- ランダムアクセスのバッチであれば、`BUFND(10)` 〜 `BUFND(30)` 程度からスタートし、SMFレコード(タイプ30や42)のI/O統計を見てヒット率を確認する。
- シーケンシャル処理主体の場合は、VSAMのCI(Control Interval)サイズやCA(Control Area)サイズ、さらにJCL側の `AMP=’BUFND=nn’` の指定との兼ね合いもあるため、過大なバッファは逆効果になることがある。
—
5. デバッグとトラブルシューティングのコツ
もし、あなたがチューニングを行った結果、予期せぬエラーに直面したときは以下のポイントを確認してほしい。
1. JCLのAMPパラメータとの競合:
PL/Iの `ENVIRONMENT(BUFND(15))` は非常に強力だが、もし実行JCLの `//DDNAME DD` カードに `AMP=’BUFND=30’` のような記述がある場合、JCL側の指定がPL/Iソース側の指定を上書き(オーバーライド)する仕様になっている。「ソースを直したのにバッファ数が変わらない!」と悩んだときは、JCLのDDカードを疑え。
2. ONユニットによる例外管理:
VSAMの容量不足やキー重複(KEYERROR)、レコード不在(KEYCONDITION)は、必ずPL/Iの `ON` 条件で捕捉できるように設計すること。特にバッファ拡張時はメモリ割り当てに関する異常(STORAGE条件など)が発生するリスクもわずかながら高まるため、堅牢なエラーハンドリングがプログラマの腕の見せ所だ。
—
シニアアーキテクトからのメッセージ
レガシーシステムの寿命は、私たちが思うよりも長く、そしてビジネスの根幹を支え続けている。PL/Iという言語は、ハードウェアの特性(メモリ構造やI/Oチャネルの挙動)をダイレクトにコードへ反映できる、極めて洗練されたツールだ。
「なぜこの数値(BUFND/BUFNI)を設定するのか」という根拠を論理的に説明できるようになれば、君も立派なメインフレーム・アーキテクトだ。
日々のバッチウィンドウ短縮の戦いに、この知見を役立ててほしい。健闘を祈る!
