なぜRDBからCSV + COBOLに変更する事でコスト削減と高速化を同時に実現出来たかの考察

TLを見てると以下の記事が少し話題になってました。 tech.nikkeibp.co.jp tech.nikkeibp.co.jp

対象の記事は有料会員じゃないと見れないのだけど事例としては以下みたい。

リソース - ユーザー事例 - COBOL製品 ユーザー事例 : マイクロフォーカス

さて、この記事の驚きポイントは「1億レコードくらいのDB処理をRDBからCOBOL + CSVに変更してUnixサーバからWindowsサーバに変える事で性能を維持しつつコストを1/5くらいにした」という事でしょう。 「せっかく7割もあったSQLを全部COBOLに変えるとか時代に逆行しすぎ!」とか「RDBをファイルに変えるなんてとんでもない」とか思う人も多いんじゃないでしょうか。とりあえず私は思いましたw

しかし実際に高速化はありえるのか、その理由を分析していきましょう。

そもそも既存はどんなロジック?

中の人じゃ無いので分かりません! 分かりませんが、推測はできます。

1年分が約1億レコードに迫るデータ量で、それを1度に数年分扱う大規模システムである 毎月、会員保険会社から契約情報、保険金支払情報などのデータを収集、機構内でデータを処理した上で料率計算の基礎データ作成を行います

月次処理として過去情報から利率を求めるってことは本質的にオフラインバッチです。つまり、トランザクションの考慮は不要です。

もともとこれはメインフレームで稼働していました。〜中略〜 2003年にUNIX環境へ移植 COBOLシステムは日本IBM製のメインフレームで稼働しており、保守コストの高さも問題になっていた。 その内訳はSQLが7割、COBOLが2割、C言語が1割。SQLのユーザー定義関数を利用し....

直接の移植はUnixからですが、ベースのビジネスロジックメインフレームで作成されていた事が分かります。 SQLが7割もあるのでハッキリとは断言できませんが、メインフレームで作っているならベースのビジネスロジックはIMSのような階層型データベースやVSAMといったファイルベースの可能性があります。 その場合、RDBを使っていても単にキーでデータを取得する単なる箱になっている可能性は高いです。

というのもIMS/VSAMはSQLのような条件付きデータ取得ができません。そのため典型的なメインフレームでのDB操作は「全てのレコードを読み込んでプログラムで柔軟に条件判定を行う」という作りをしています。

疑似言語 + SQLで書くと以下のような処理は

sql = "SELECT SUM(amount) FROM hoge WHERE flag == 3"
result = db.exec(sql)
puts result

メインフレーム的なアーキテクチャだと以下のように書く必要があります。

def isValid(record)
    record.flag == 3
end

sum = 0
db.get('hoge').records.each{|record|
    if isValid(record)
        sum += record.amount
    end
}

全部データをPG側で舐めているのでフルスキャンと同等、いえネットワークコスト等も加味するとそれ以上の負荷がかかる事は容易に想像できるでしょう。 公開されてる情報からは想像の世界を出ませんが、メインフレーム時代から使ってるならこのような構造の可能性は高く、今回はその方向で考えます。

RDBなんだからWhere句使ったら?

さて、既存のビジネスロジックが前述した通り総ナメ処理だと仮定し、性能が足りないという状況。皆さんならどう改善しますか? ほとんどの人は「ちゃんとRDBに仕事をさせろ、WHERE句使え。最悪インデックスが無くてもそっちのが爆速」と考えるでしょう。 王道ですね。しかし、単純にそうはいかないケースが多々あります。

例ではisValidはシンプルにレコードの中の特定カラムの値を比較するだけでした。 しかし、実際は遥かに複雑な条件判定ビジネスロジックが幾重にも使われてる可能性が高いです。別のテーブルの値と相関比較は当たり前に出てきます。そもそもそもそもDBの数字型や文字列型では無く、COBOLのデータ型のバイナリがそのままRAWとして突っ込まれている可能性すらあります。古いコードはデータ容量節約のためにboolean的なフラグとか使わずにビットをシフト演算で判定するとかあるあるですし。 こうなってくると単純にWHERE句に入れるのは難しい。

根本的なビジネスロジックを考え直すか、JOINやCASE文を駆使してSQLに無理やり判定処理を移植するしか無いです。そして、後者はフルスキャンになりがちですし性能が伸び悩むこともあるんじゃ無いかと。 ユーザ定義関数を色々使われてるし、7割もSQLがあるということは割と後者寄りの対応が元々はされていたのかもしれないです。

前者の根本対応が出来ればベストですがそれは基本設計からやり直しなので、移植や更改レベルのコストだと割に合わない可能性は高そうです。

なぜファイルにすると速くなるのか?

最初からSQLを前提としたビジネスロジックでは無く、RDBを使ってるとしても単なるキー検索の箱か無理やりSQLで複雑なロジックを組んでる可能性が高いのでRDBの特徴である集計処理を活用できてない可能性を述べました。

では、なぜファイルにすると速くなるのでしょうか? まずファイルといってもJSONXMLなどのような柔軟な構造化データでは無く、おそらく固定長データ処理を行なっています。そのためシリアライズが極めてシンプルなので高速化が期待できます。 固定長というのは「1レコードは10バイトあって「1バイト目はID」「2から5バイト目は項目名」「残りの5バイトは数量」みたいな暗黙のルールでデータを記述していき、読むときにルールに従って前から順に解釈していく」という方式です。今でもHDFSの内部データとか高速性を要求される部分には普通に使われている技術です。

余った項目は空白とか0で埋めます。CSV自体は可変長のデータフォーマットですが、固定長的にこれを処理してるんじゃ無いかと思われます。 というのも固定長データとCOBOLのレコード構文は非常に相性がいいためです。

固定長でファイルを処理している場合はRDBに比べてレコード読み込みは非常に高速です。 まず、SQLや同時アクセス/トランザクション周りのコストがありません。またネットワーク通信では無いのでI/Oも高速です。

加えて、COBOLで固定長を扱うなら「シリアライズ」が不要です。ほとんどのCOBOLではレコードのメモリの中身をそのままファイルに吐き出すのを基本とします。 これはある種のバイナリフォーマットですが*1シリアライズのコスト無くデータを読むことができます。

加えてCOBOLコンパイル言語なのでわりかし速度は速い方です。 複雑な条件判定自体もSQLで無理やり実装するよりは単純に高速化される可能性は高いです。

これらのポイントにより例え全データを舐める処理になったとしても、最適な利用をされて無いSQLより大幅に高速化される可能性は十分にあります。

並列化と分散処理による高速化の可能性

今回使ってるかが分かりませんが、データを例えば種別とか年月で分割して処理できるなら並列処理/分散処理による高速化も期待できます。 トラディショナルなRDBは同時リクエストを受けるのは得意でも一つの処理を並列処理するのはさほど得意では無い気がします(パラレルクエリとかもあるけれど)。加えて根本的にスケールアウトさせずらいアーキテクチャというのもあります。

そのため、そういった処理が可能なデータ特性/ビジネスロジックであった場合は単純にマルチスレッド、ジョブの並列化、あるいはHadoop/Sparkの活用*2することで高速化を狙うこともできます。 Unixサーバ(AIX?) Windowsサーバになったのでクラウド化や社内でもvmwareを使ったスケールアウトは容易になるでしょう。実際、テスト環境は作りやすくなったとあるし。

COBOL使う必要あったの? Javaとかじゃダメだったの?

これは出てる情報だけからはなんともですね。一応記事中には「バッチ処理がメインのためJavaでは速度が出なかった。」とありますが個人的な認識としてJavaはそこまで遅い言語では無い。 考えられるのはプロジェクトの経緯的にJavaの時点ではRDB撤廃は決まってなかったのでRDB + Javaが比較の対象であるとか、COBOLや他のレガシーとの互換性を保つために検討したFWが重いとかそういう方向じゃ無いかという気がします。

JavaやあるいはRubyなんかを使ってリライトしても「CSVじゃ無くてKryoやMassagePackのような高速なシリアライザを使う」「個々のリライトコードではCOBOLらしさとか互換性は捨てる」という事をすれば結構速度出たのでは? という気がしなくも無い。

たぶん、単純な速度以上に他言語への移植コスト/移植後のメンテコストを加味した結果かな、という気もしています。COBOLの開発に慣れてるなら人も活かせるし。 COBOLにコミットした開発体制を維持するならCOBOLに無理がない範囲で集約するのは一つのジャッジで、5年程度の延命としては十分かな。 10年後、20年後は技術者もさらに減るだろうし分からないけどCOBOL「私を殺すと言ってた言語は、みんな死んだよ」 | おごちゃんの雑文という話もあるので、大丈夫な気もする。

まとめ

さて、色々仮説を重ねてはいますが「RDBをやめてCSV + COBOLにしたら高速化した理由」を考察してみました。あってるかどうかは知らないですが、大きく外してもないんじゃないかと。 「ファイルやCOBOLにするとか時代の逆行!」と思いがちですが、新しいものが常に最適解とは限らないので多くの技術を学んで自分たちの状況に最適なものを選んでいきたいですね。

どうでも良いけどCOBOLの移植先としてはRuby(特にJRuby)が、Decimalも簡単に扱えるし比較COBOL的なシンプルさと柔軟な抽象化を両立できると思うので結構良いと思ってる。JavaよりCOBOLっぽく書きやすいと思う。フレームワークは作る前提だが。 だから、金融系SEはもっとRubyに可能性を感じるべき!

それではHappy Hacking!

*1:COBOLの場合は文字列や数値をテキストとして内部データも保持してるので結果として一部をのぞいてテキストになります。ASCIIかはさておき

*2:COBOLHadoopで動かす話はそれなりに事例がある。