SHISAMの極意:なぜ現代のアーキテクトが今あえて「単純な階層構造」に立ち返るのか
こんにちは。チーフアーキテクトの私だ。
今日のコードレビュー、あるいはアーキテクチャ設計レビューで、また「とりあえず何でもRDB(あるいはNoSQL)のドキュメント指向で」という安易な設計を見かけて溜息をついたところではないか?
「マスター・ディテール構造のデータだからリレーショナルに」「いや、スキーマレスなJSONをそのまま突っ込めるからNoSQLだ」――ちょっと待て。本当にそのデータ構造、複雑な結合やアドホックなクエリが必要か? アクセスパスが完全に固定されており、ミリ秒単位の極限スループットと、物理I/Oの完全な制御が求められる領域において、私たちは先人たちが築き上げた最も純粋なアーキテクチャの一つを忘れていないか。
そう、SHISAM(Simple HISAM)だ。
今回は、IBMのIMS(Information Management System)などに代表される階層型DBMSの世界において、HISAM(Indexed Sequential Access Methodベースの階層構造)を極限までシンプルに削ぎ落としたSHISAMに焦点を当てる。
「レガシーな技術」と侮るなかれ。データの局所性(Locality)、ポインターチェーンの排除、そして物理シーケンシャルアクセスの極意を理解している者にとって、SHISAMは現代の超高速KVSや組込みストレージエンジンを設計する上でも極めて強力なインスピレーションの源泉なのだ。
実務で明日から使える堅牢な設計パターンと、パフォーマンスの罠をロジカルに伝授しよう。
—
1. SHISAMの本質:なぜ「ルートのみ」が最強の武器になるのか
まず大前提を整理する。通常のHISAM(Hierarchical Sequential Access Method)は、論理的な階層構造(ルート・セグメントの下に子、孫セグメントがぶら下がる形)を、VSAM(KSDS:Key-Sequenced Data Set)等のインデックス付き順編成データセット上に「物理的な連続領域」としてマッピングする手法だ。
しかし、SHISAMはその名の通り、これを極限まで単純化したものだ。
厳密には、SHISAMは「ルート・セグメントのみ」で構成されるデータベース、あるいはそれに準ずる非常にフラットな階層構造を対象とする。
[従来のHISAM: 複数レベルのセグメント]
ルート [Key: 001] —> 子セグメントA [Key: 101] —> 孫セグメント [Key: 901]
|
+—> 子セグメントB [Key: 102]
[SHISAM: ルートセグメントのみ(事実上のフラット構造)]
ルート [Key: 001] (データブロック内にすべての属性を保持、または単純なオーガナイゼーション)
ルート [Key: 002]
ルート [Key: 003]
「それってただのKVSや、単一テーブルのRDBと何が違うのか?」という声が聞こえてくる。
違う。OSのアクセスレイヤーとインデックス構造、そして物理バッファプールの占有戦略が完全にコントローラブルである点が決定的に違うのだ。
SHISAMでは、階層の深さが「1(ルートのみ)」であるため、親子間のポインターチェインを辿るオーバーヘッド(いわゆるデータベースエンジン内部のポインター・スィズリング)が一切存在しない。キーを指定すれば、VSAMのB-Treeインデックス(RBA/RRNの解決)を経て、一撃で目的のデータブロック(CI: Control Interval)へ到達できる。
—
2. スキーマ定義(DDL相当)と物理設計の勘所
階層型DBMSにおける定義は、データの論理構造と物理的なレコードレイアウトを直結させる必要がある。コードレビューで「この物理設計はヤバい」と見破るためのポイントを交え、仮想的な定義例を見てみよう。
- =====================================================================
- SHISAM データベース定義 (DBD) の概念モデル
- 対象: 口座マスタ (Account Master)
- アクセスパス: 口座番号 (Account-ID) による完全な一意索引アクセス
- =====================================================================
DBD NAME=ACSTSHIS, ACCESS=SHISAM
DATASET DD1=ACSTDS01, DEVICE=3390, BLOCK=4096
- ルートセグメントの定義 (SHISAMでは唯一のセグメント階層)
SEGM NAME=ACSTSEG, PARENT=0, BYTES=256,
PTR=NOTHING
- フィールド定義 (キーと属性)
FIELD NAME=(ACCKEY,SEQ,U), BYTES=10, START=1, TYPE=C
FIELD NAME=ACCNAME, BYTES=60, START=11, TYPE=C
FIELD NAME=BALANCES, BYTES=16, START=71, TYPE=P
FIELD NAME=FILLER, BYTES=169, START=87, TYPE=X
END
チーフアーキテクトからの設計レビュー指摘:
1. `PTR=NOTHING` の美学:
子セグメントを持たないため、ポインター領域(4バイト〜8バイトの物理アドレス)をセグメント接頭辞(Prefix)に持たせる必要がない。純粋にデータ領域のみにバイト列を割り当てられるため、パディング効率が極限まで高まる。
2. CI(Control Interval / ブロック)サイズの選定:
上記の例では `BLOCK=4096`(4KB)としている。もし1レコードが256バイトなら、1つのCIに約16レコードが綺麗に収まる。VSAM KSDSのCIスプリット(分裂)を最小化するため、レコード長とCIサイズの倍率関係は徹底的に計算せよ。勘で決めるプログラマは即座に現場から外せ。
—
3. 実務における使用例:なぜ「今」SHISAM的アプローチが必要なのか?
「マスター・ディテール」のデータをあえてSHISAMのようにフラット化する、あるいはルートのみで表現するシステムとはどのようなものか。
典型例は、「極限のスループットが要求される金融の勘定系残高テーブル」や「IoTデバイスのメタデータ・ストア」だ。
複雑なJOINや、親子をまたぐアドホックなクエリ(例:「過去3ヶ月間に取引があった全顧客の子どものデータを集計しろ」のようなもの)が絶対に発生しないというビジネス要件がある場合、RDBの正規化は百害あって一利なしとなる。
アプリケーションコード(アクセスパターン)の模範例
C言語やCOBOL、あるいはモダンなバッチ基盤からSHISAMへアクセスする際のイディオムを疑似コードで示す。
/
- SHISAMルートセグメントのランダム・アクセス (GU: Get Unique)
- データベースエンジン内部でのポインター走査がゼロである点に注目せよ。
/
void fetch_account_balance(const char target_acc_key, AccountRecord out_rec) {
// パラメータリスト(PCB)の設定
IO_PCB pcb = get_account_pcb();
// 探索キーのセット
memcpy(pcb->search_arg, target_acc_key, 10);
// SHISAMに対するダイレクト・リード (GUコール)
// 戻り値: 0 = 成功, 状態コード (Status Code) による厳密な制御
int status = db_call(“GU”, pcb, out_rec);
if (status == DB_STAT_OK) {
// ヒット: 物理I/O最小限でデータ取得完了
process_record(out_rec);
} else if (status == DB_STAT_NOT_FOUND) {
// 該当キーなし
handle_not_found(target_acc_key);
} else {
// 致命的なI/Oエラーまたは構造異常
handle_fatal_error(status);
}
}
このコードの美しさは、「無駄なレイヤーが一切ない」ことだ。SQLのパース、オプティマイザによる実行計画の選択(テーブルスキャンかインデックススキャンかの迷い)、ロック競合の評価などというオーバーヘッドは、SHISAMの世界には存在しない。指定されたキーに基づき、VSAMのインデックスを引いて実データブロックを直撃する。それだけだ。
—
4. パフォーマンス上の罠と堅牢な運用パターン
しかし、アーキテクトとして警鐘を鳴らしておかなければならない。SHISAMはその「シンプルさ」ゆえに、設計を誤るとシステム全体を致命的な破滅に導く。
罠1:VSAM KSDSの「CIスプリット(Control Interval Split)」と「CAスプリット」
SHISAMはベースとしてKSDS(Key-Sequenced Data Set)を使用するため、ルートキーの順序に依存する。
もし、アプリケーションが完全にランダムなキー(例えばUUIDやハッシュ値)を生成してSHISAMにインサートし続けた場合どうなるか?
- 結果:物理ブロック(CI)の空き容量が均等になくなり、ひっきりなしにCIスプリットが発生する。物理的なディスク上のページが断片化し、シーケンシャル・スキャン性能が完全に崩壊する。
【解決策:堅牢な設計パターン】
- サロゲートキーの設計:キーには必ず「時系列でインクリメントされる値」または「プレフィックスに日付やパーティションIDを持つ値」を採用し、物理的なアペンド(追加)が局所化するように設計せよ。
- 定期的な再編成(Reorganization):バッチウィンドウにおいて、定期的にアンロード/ロード(Reorg)を実施し、物理的な連続性を担保する運用フローをCI/CDパイプラインならぬ「運用パイプライン」に組み込め。
罠2:可変長セグメントの扱いの誤り
「ルートのみだから、ついでに可変長(Variable-length)にして大きなJSONでもぶち込んでおこう」――これを見た瞬間、そのエンジニアの頭を叩き割らなければならない。
SHISAMで可変長セグメントを多用すると、物理ブロック内のパッキング効率が狂い、オーバーフロー・チェーンや断片化の温床となる。
【解決策:固定長ファーストの原則】
- データのコア部分は完全固定長(Fixed-length)で設計し、溢れた拡張データは別トライブ(あるいはオーバーフロー領域)に逃がすか、そもそもSHISAMの適用範囲外と見なせ。SHISAMの強みは「予測可能な物理レイアウト」にある。
—
5. チーフアーキテクトからの最終提言
現代のソフトウェアエンジニアは、「抽象化の階層」に毒されすぎていないか?
Frameworkの上にORMを重ね、その上にドキュメント指向の抽象化層を置き、遅くなったらスケールアウトすればいいと笑う。
だが、極限のトランザクション処理、ミリ秒を争う金融勘定系、あるいは限られたリソースで稼働するエッジコンピューティングの世界において、「ハードウェアの物理特性とメモリ/ディスクの局所性(Locality)」を直結させる技術は永遠に廃れることがない。
SHISAMは、階層型DBMSの歴史的遺物などではない。
「無駄を極限まで削ぎ落とし、キーと物理ストレージを最短距離で結ぶ」という、高パフォーマンス・アーキテクチャの原点なのだ。
次にデータベースの設計図を書くとき、自問してほしい。
「本当にその複雑なリレーションやスキーマレスな柔軟性は必要なのか? 俺たちは今、SHISAMの美しさを選ぶべきではないのか?」と。
健闘を祈る。厳格で妥協のない設計を期待している。
コメント