昨今のDBMSの分野の進歩はGoogleがパンドラの箱を空けてからずっと凄いスピードで進化し続けているが、中でもVoltDBは飛び抜けたOLTP性能に特化したDBMSだ。しかしそのプロトタイプであるH-Storeからもう何年も経っており、当然その先の何か、というのが見えてくる。

最近久々に技術に立ち戻ってちょっと最新のDBMSに関する論文を読むか、とカラム指向データベースの圧縮手法について学ぼうかとStonebraker教授の前作C-Store(後のHP Vertica)を調べたり、ハイブリッドDBの前段に使われるインメモリDBの構造について勉強しようと思ったりしてたら非常に興味深いシステムを見つけたので紹介したい。それはミュンヘン工科大学のHyPerだ。HyPerはH-Storeのあとを受け、そのOLTP性能の高さを受け継ぎながら、OLAP性能に関してもインメモリDBならではのずば抜けた性能を発揮できるシステムになっている。

超絶OLTP性能を実現するための三種の神器、非同期I/Oモデル・オンメモリ・ストアドプロシージャのみ許可


ここでVoltDBがなぜありえないほどのOLTP性能を持っているか、というところを確認しておきたい。それはひとつにインメモリDBであることだが、もう一つ重要なことがある。VoltDBでは非同期I/Oを使ったクエリ実行モデルを備えていることだ。非同期I/Oイベント駆動モデルはすでにnode.jsとかでその優秀さ、特にレイテンシの低さを証明しているが、VoltDBは更にその上を行っている。

RDBMSにおいては、マルチスレッドモデルでDisk I/Oの高いレイテンシを隠蔽する一方で、古典的なロックモデルによる悲観的ロックを用いて競合する更新を排除するのが基本方針だ。ところがインメモリDBは高いレイテンシを持つI/Oは無い。するとマルチスレッドを採用する必要がない。となるとロックを取る必要もない。だから非同期I/Oで十分だ!ということになる。一見先祖返りだが、個々のクエリの実行時間が短ければこれはうまくいく。しかもロックを取らなくて良くなることで、さらに実行時間が短くなる。

更に上手く行かせる工夫として、すべての実行可能なクエリを原則事前コンパイルしたストアドプロシージャにすることで、1つの問い合わせが自動的に1つのトランザクションを構成するようになっている。その上ネットワーク上でインタラクティブなやり取りが発生するのも抑えている。なんとなれば、begin一本投げるだけでも、ネットワーク越しでは0.1ms程度かかるわけで、これは従来の遅いRDBMSなら無視出来るレベルであったが、100000tpsを目指そうとする非同期I/ODBMSでは無視できない巨大な損失である。

実にVoltDBのオンメモリ非同期I/Oというモデルは一石三鳥の素晴らしい選択であったことがわかる。これを更に改良していこうというのがHyPerの目的なわけだが、どう切り込んでいくか見てみよう。

非同期I/Oを補助するマルチスレッド


とはいえ、単純非同期I/OでマルチコアCPUの恩恵は受けられないし、メモリ帯域を使いきれるわけでもない。HyPerの改善点の一つは、読み込みのみのOLTPクエリを並列処理するためのマルチスレッドモデルを非同期I/Oの下に用意しているところだ。非同期I/Oモデルによるロッキングは、確かに同時に他の書き込みSQLを実行させないことを求めている。しかし読み込みは別に制限されない。だから、読み込み専用の場合のみ、マルチスレッドで実行させてしまえば良いわけだ。

この改善で本来非同期I/Oモデルが苦手とするマルチコアCPUの性能を使いきりつつ、なおかつ読み込み専用トランザクションの性能を大きく改善できる。それゆえHyPerのOLTP性能は、VoltDBに比べても際立っている。ただし最近VoltDBも相当改善したはずで、そこと比べるといい線いっているかもしれないが。

非同期I/OモデルとOLAPが共存しないわけと、その解法とは


このVoltDBの非同期I/Oモデルは、とにかくあらゆるSQLが超短期間で終了することを前提としている。それゆえ、OLAPクエリととてつもなく相性が悪い。巨大なOLAPクエリはシングルスレッドを専有し、あらゆる書き込みをその間遮断してしまう。OLAPクエリの大半はマルチスレッドで実行させても大丈夫なものが多いが、非同期I/Oでやってしまうと、単にCPUを使いこなせない上にずっとロックするろくでもないシステムにしかならない。故に、VoltDBではOLAPクエリを実行しないことを求めている。これはVoltDBの設計の致命的な部分で、ただそこを犠牲にすることによりOLTP性能を実現しているわけでもある。

HyPerの凄いところは、この致命的な欠陥を回避する解法として、Virtual Memory Snapshotという手法を使って、HyPer内部のインメモリDBの前データのスナップショットをとっている。このスナップショットに対しては更新もされない。心置きなく自分のOLAPクエリを実行できる。スナップショットを取るタイミングは、全くトランザクションが発生していないタイミングで、なおかつ遅延されている更新が存在しない状況に限る。そんな都合のいいタイミングがあるか、というと非同期I/Oではトランザクションが同時に1個しかはしらないから、いつでも発生するのである。そこで魔法の呪文を唱えれば、OLAP用のバックエンドがハイ完成。

ちみにこのOLAP用バックエンドは完全なコピーであるから、バックアップもこのスナップショットから直接作ることができ、容易である。

古の魔法の呪文、その名はfork()


さて、この随分手の込んでいるように見えるVM Snapshotという技法だが、実はずっと昔から存在している。あるプロセスのメモリを丸々コピーし、互いに干渉しない2つのプロセスに分割させる手法、そう、UNIXのfork()そのものだ。今時のfork() はshadow pagingというかcopy-on-writeなので、実際にはコピーすらせずに同じメモリ領域を見ることになる。その御蔭でコピーも最小限、変更した時の挙動も最小限。実に言うことがない。

つまり、OLAPクエリがやってきたときは、非同期I/Oの中で処理せず、マルチスレッドも使わず、そのまま内部でfork()を実行してそっちに処理を引き渡すだけなのである。これが傑作なのは、fork()は結構重いAPIだと昔からみなされており、かつてのMySQL vs PostgreSQL論争では、良くマルチスレッドのMySQLのほうが性能がよく、fork()を使うPostgreSQLの性能が良くない、と言われてきたところにある。かつてあれほど非難されたfork()が、今度は性能向上の切り札として再登場するのだから痛快である。目から鱗が落ちるとはこのことだ。

まとめ:HyPerの見どころは、非同期I/O・マルチスレッド・forkのハイブリッドプロセスモデル


というわけで、HyPerとはVoltDBをベースに、単純非同期I/Oからプロセス/スレッドモデルをハイブリッド化することで更なる多様なワークロードに対応できるように進化したシステムである。なお、この手法を持ってしても、巨大な更新クエリ、例えば全件Updateのようなものには対応できるようにはならない。もともとこういうクエリはかなり特殊な部類で、リレーショナルモデルの完全性を思い知る一方で、性能向上にはやさしくない機能だなとよく思っていたが。

なお、残念ながら、ソースコードは公開されていないようである。2010年代にふさわしい?見事なシステムと思うが、この成果が利用できるのはまだまだかなり先かもしれない。