本エントリはMapR社の Carol McDonald による2017年9月のエントリー、 Database Comparison: An In-Depth Look at How MapR-DB Does What Cassandra, HBase, and Others Can't の和訳になります。
MapR-DBのみならずMapR-FSのベース技術に関する低レイヤーの挙動の解説を含む良記事なので、翻訳したい旨をCarolに伝えたところ、快諾を頂いたのでこちらで紹介させていただきます。
投稿から時間が経過しているので、Cassandra, HBaseの挙動については既に変更されている点があるかもしれませんが、こちらについてはご容赦お願いします。
MapR-DBの挙動については変更ありません。
高い性能を要求するプロダクトに関わる開発者やアーキテクトであれば、何が違いを生むのか理解する必要があります。
これはレーサーが性能の良いレーシングカーを選ぶ時と同じです。
この点において、私が以前書いた詳細なアーキテクチャに関するエントリー、An In-Depth Look at the HBase Architecture や Apache Drill Architecture: The Ultimate Guide、 How Stream-First Architecture Patterns Are Revolutionizing Healthcare Platformsは非常に好評だったので、今回も楽しんでもらえればと思います。
今回のエントリではMapR-DBのアーキテクチャの詳細について、同じNoSQLアーキテクチャを持つCassandraやHBaseと比較した形で紹介します。
そして、MapR-DBが即時リカバリやゼロデータロスといった機能を持ちつつ、高速で一貫性があり、スケーラブルなパフォーマンスをどのように実現しているのか説明します。
Why NoSQL?
簡単に言うとNoSQLを選択するモチベーションはデータのボリューム、速度、そして(もしくは)多様性です。
MapR-DBではデータの多様性のため2つの異なるモデルを提供しています。
- HBase APIを持つワイドカラム型データモデル
- (MongDB APIに類似した)Open JSON APIを持つドキュメント型モデル
ボリュームと速度に関して、最近のESG Labsによる分析では以下のオペレーション/秒に示すように、クラウド環境でCassandraやHBaseよりも10倍近く速いという結果が出ています(値が大きいほうが性能が良いことを示します)。
スケール性能を持たせつつも高速な読み書きを実現するには?
ポイントは並列実行のためにパーティショニングすることと、ディスクの読み書きに消費される時間を最小化することです。
リレーショナルモデルの限界について
リレーショナルモデルを使う上では、(データの格納時に)スキーマを正規化して冗長なデータを排除し、ストレージを効率的に使用します。
そして、インデックスとjoinクエリを使って必要となるデータを再度持ち寄って処理します。
しかしながら、インデックスの作成は逐次的でないディスクI/Oを多く発生させるため、データの挿入に時間がかかります。
また、joinにより大量のデータの読み込みが発生しボトルネックになります。
そのため、リレーショナルモデルはクラスタ内で水平にスケールしません。
ロウキーにより自動的にパーティションされるよう設計されたMapR-DB
HBase APIを使うにせよJSON APIを使うにせよ、MapR-DBではテーブルがキーのレンジによってクラスタ内で自動的にパーティションされます。
そして、クラスタ内のそれぞれのサーバがテーブルのサブセットのためのソースを持ちます。
MapR-DBは"クエリファースト"なスキーマデザインを持ちますので、まずはどのようなクエリを実行するかを考慮してください。
その後データが一定に分散し、かつ、効果的なプライマリーインデックスを持つようロウキーをデザインしてください。
ドキュメント(JSON)にしろカラム(HBase)にしろ、ロウは読まれるデータが同時に含まれるようにグループが設計されるべきです。
MapR-DBではスキーマを非正規化して一つの行もしくはドキュメントに、リレーショナルな世界でインデックス化された複数テーブル分のデータを格納します。
キーレンジによりデータをグルーピングすることでロウキーによる高速な読み書きを実現することが出来ます。
HBase APIを持つMapR-DBのスキーマデザインのさらなる詳細に関してはこちらを参照してください。
NoSQLはどのように高速な書き込みを行うか
B木をベースとしたこれまでのデータベースでは非常に速い読み込みを可能にしますが、Disk I/Oが組織化されておらず非効率なため、更新をリアルタイムに行うにはコストが高くなります。
(画像はWikipediaより)
一方、Log Structured Merge木(LSM木)では逐次的でないディスクI/Oの発生を最小化することで、これまでのB木ベースのDBよりも高い書き込みのスループットを提供するよう設計されています。
LSM木はHBase, Cassandra, MongoDBといったデータベースでも使われています。
LSM木はディスクとメモリ上のログに更新を書き込みます。
更新データはリカバリのためだけに利用されるログファイルに追記され、メモリ上でソートされます。
memory上の書き込みデータの格納領域をすべて消費すると、ディスク上のイミュータブルな新しいファイルへとフラッシュされます。
Read Amplification
LSM木のデザインは高速な書き込みを実現しますが、もしクエリ対象のロウのデータがメモリに存在しない場合、より多くのファイルがディスクに書き込まれるに連れて、対象のロウデータを取得するために複数のファイルを検索する必要性が出てきます。
この場合読み込みの速度は低下します。
(訳注:これをread amplificationと呼びます)
Write Amplification
読み込みの高速化のためには、バックグラウンドのプロセスが、コマンドもしくは定期的に、複数ファイルを読み込み、メモリ上でソートしてマージし、新しくより大きなファイルの中にkey-value形式でソートして書き込むことでコンパクト化する必要があります。
しかしながら、このコンパクションは多くのディスクI/Oを発生させて書き込みのスループットを低下させるため、 write amplification と呼ばれます。
Cassandraがコンパクションを行う際には50%のフリーなディスクが必要となり、コンパクションによりディスクスペースが不足した際にはOSが止まることになります。
Read/Write amplificationはHBaseとCassandraにおいて予測不能なレイテンシの上昇を招きます。
ESG Labsの最近の調査によると、MapR-DBはCassandraとHBaseよりも最大で10倍の性能向上が見られ、レイテンシも予測可能な範囲で、かつ、低い値であったことが確認されています(下図では値が低いほど性能がいいことを示しています)。
MapR-DBではどのようにレイテンシを抑えているのか
LSM木はB木よりも高速な書き込みを実現しますが、読み込みの性能は劣ります。
また、LSM木ではコンパクションを"しなさ過ぎる"ケース(読み込み性能に影響)と"し過ぎる"ケース(書き込み性能に影響)とのバランスが重要となります。
MapR-DBではこの中間に位置取り、HBaseやCassandraのようなコンパクションの負荷を避けつつ、これまでのシステムのような高いランダムI/Oを回避します。
その結果、極めて変動の少ないレイテンシ非常に高いスループットを提供することが出来ます。
MapR-DBに出来て他のシステムに出来ないことを理解する上では、MapR Converged Platformを理解する必要があります。
Apache HBaseとApache Cassandraではappendオンリーなファイルシステム上で実行されており、データの変更に合わせてデータファイルを更新することが出来ません。
MapRがユニークなのはMapR-DBテーブル、MapRファイル、そしてMapR Event Streamsが高いスケーラビリティを持ち、信頼性があり、包括的な分散データストアであるMapR-XDとして統合されていることです。
MapR-XDはランダムなread/writeファイルシステムをC++でネイティブに実装しており、ディスクに(Ext3などのファイルシステムを介さずに)直接アクセスします。
そのため、MapR-DBは常に不変なファイルを書き込むといった挙動を取る代わりに効率的にファイルの更新をすることが出来るのです。
MapR-DBはLSM木とB木のハイブリッド型のモデルにより、一貫性があり、かつ、高速な読み書きを実現しています。
これまでのB木モデルと違い、リーフノードはアクティブにバランスされませんので、更新処理を非常に高速に実行できます。
MapR-DBでは更新処理は小さな"micro"ログにアペンドされ、メモリ上に格納されます。
新たなファイルにフラッシュするLSM木と違って、MapR-DBではメモリが頻繁にread/writeファイルシステムにマージされるときに"micro"なデータの再構築が行われます。
これはMapR-DBがコンパクションを必要としないことを意味します("micro"ログはメモリがマージされた後必要なくなりますので削除されます)。
さらにMapR-DBでは読み込み処理においてB木を活用して対象付近のデータ郡により早くアクセスし、その後これらのデータ群をスキャンして該当するデータを探します。
HBase, Cassandraでのリカバリ
logがリカバリにのみ使われることを話す前に、どのようにリカバリが行われるか説明します。
一般的なハードウェア上で信頼性を実現する上では、複数の冗長なデータのコピーが格納されるレプリケーションスキーマに頼る必要があります。
HBaseではどのようにリカバリするのでしょうか?
HDFSではファイルを64MBのブロックに分割され、それぞれのブロックがクラスタのDataNodes間にまたがって、2つのレプリケーションを持つ形で格納されます。
もしHDFSのDataNodeがクラッシュしたら、そのリージョンは他のDataNodeにアサインされます。
新しいRegionはLogの内容が再度復元された後、利用可能となります。
つまり、Logからまだファイルにはフラッシュされていない更新をすべてメモリ上に読み込み、key-valueでソートした形で新しいファイルにフラッシュします。
HBaseのリカバリプロセスは遅く、以下のような問題点を抱えています。
- HDFSが64MBという大きなBlock単位でディスクI/Oを行う
- HDFSファイルはread-onlyであり、ファイルごとに単一のwriteプロセスしかいない。ファイルへの書き込みが行われている間読み込み処理はできず、ファイルのクローズまでのトランザクション処理を行って初めて読み込み処理が可能となる。途中で失敗が発生した場合、閉じていないファイルは削除される
- HDFSとHBase間のレイヤーのため、新しいファイルが書き込まれたときにのみデータのローカリティが保証される。HBaseではリージョンがフェールーバ時に移動したりロードバランスしたデータがローカルに存在しない場合、リージョンサーバが他のノードからファイルを読み込む必要がある
- Log(もしくはWAL)は大きいので、再読込には時間がかかる
- ファイルシステムと分離されていることから、フェールオーバが発生した際にはzookeeper/hbase-master/hbase-region-server/NameNode間でコーディネーションが必要になる
Cassandraの場合はどうでしょうか?
Cassandraの場合はクライアントが書き込みの要求をクラスタ内のいずれかのノードに送信し、これがクライアントへのプロキシとして動作します。
このプロキシノードはデータレプリカを持つNノードの場所を調べ、それらすべてのノードに書き込み命令を転送します。
もしノードが死んだ場合、コーディネータは他のレプリカに書き込みを続けるので、失敗したノード上のレプリカは一貫性がない状態になります。
Datastaxによると、ダウンしたノードがCassandraにおけるデータの非一貫性のよくある原因として挙げられ、エントロピーを修復するツールの実行によりルーティーンで修正される必要があります。
YammerのRobert Yokotaは、Cassandraは彼らが持つ別の一貫性の強いシステムに比べて信頼性がなく、さらにCassandraは一貫性のなさから扱うのが難しかったと報告しています。
MapR-DBがデータロス無しでどのように即座にリカバリ出来るのか
MapR-DBに出来て他のDBに出来ないことを理解するには、24/7の信頼性をデータロス無しで実現するMapRファイルシステムを理解する必要があります。
通常のオペレーションではクラスタは入ってくるデータを出来るだけ早く書き込みレプリケートする必要があります。
ファイルがMapRクラスタに書き込まれる時、まずシャーディング用に'チャンク'と呼ばれる単位で分割されます。
それぞれのチャンクは2gig/秒で8KBのブロック群としてコンテナに書き込まれます。
チャンクが書き込まれると、一連のブロック群としてレプリケートされます。
この動作はファイル全体が書き込まれるまでそれぞれのチャンクに対して繰り返されます(MapR-XDのビデオにてこちらの動作のアニメーションがあります)。
MapRではすべての書き込みがクラッシュなどなく実行されたことを保証します。
HDFSがシャーディング、レプリカのロケーション、ファイルI/Oに単一のブロックサイズを使う一方で、MapR-XDでは3つのサイズを使います。
これらのブロックサイズを区別することの重要性は、以下のようにブロックがどのように使われるかに表れます。
- シャーディング用のサイズをより大きくすることは、ファイルに付随するメタデータのフットプリントがより小さいことを意味します
- レプリカのロケーションのサイズがより大きいことは、CLDBに対するレプリカのロケーションの問い合わせ回数がより少ないことを意味します
- ディスクI/Oのサイズが小さいことはファイルがランダムに読み書きされることを意味します
MapR-DBのテーブルでは、HBaseと同様に、連続したロウがRegionもしくはTabletといった単位に分割されますが、異なる点としてはMapR-DBではTabletがコンテナの中に"存在する"点となります。
テーブルはファイルシステムに統合されているため、MapR-DBではデータの局所性を保証することが出来ます。
一方で、HBaseではファイルシステムとセパレートされているため、これを常に保証することは容易ではありません。
ノードがダウンした際には、CLDBによってそのノードに存在したプライマリなコンテナがそのレプリカへフェールオーバします。
この新たなコンテナがMapR-DBのテーブルやファイルのプライマリコンテナとして動作します。
他のDBとの最も大きい差別化要因は、MapR-DBが小さいmicro LOGsのリプレーを行うことなしにクラッシュから即リカバー出来る点であり、つまり、リカバリプロセスが実行されている間もユーザがデータベースにアクセス出来る点です。
もしmicro LOGの適用が必要なデータリクエストが行われた場合、250ミリ秒程度で実施されますので、ほとんどのユーザにとっては気づかないレベルで高速に実施されます。
HBaseやCassandraでは大きいリカバリLOGを使う一方で、MapRではTabletごとに40程度の小さなLOGを使います。
昨今ではサーバはよりたくさんのコアを持つので、出来る限りたくさんのコアで小さなLOGを書き込みリカバリを並列化することは理にかなっています。
また、MapR-DBでは常にmicro LOGの適用が行われているので、ほとんどのLOGは空という状態になります。
このアーキテクチャは、MapR-DBはHBaseよりも100倍から1000倍の速さでクラッシュからリカバリすることが出来ます。
MapRではこの動作を"Instant Recovery"と呼びます。
コンテナの中に存在するMapR-DBタブレットの特徴を要約すると以下のようになります。
- データ局所性の保証: テーブルデータはコンテナが存在するノードと同一のノード上に存在することが保証されます
- コンテナのレプリカを使う、より賢いロードバランシング: もしタブレットがロードバランシングのために移動する必要がある場合、データが以前ローカルに存在することが保証されるコンテナレプリカに移動します
- コンテナレプリカを使うより賢くシンプルなフェールオーバ: タブレットがフェールオーバ用にリカバーされる必要がある場合、コンテナレプリカからリカバーされますので、LOGとテーブルデータからなるデータがまだローカルであることが保証されます
MapR Data Platform
3つのコアサービス、MapR-XD, MapR-DB, MapR-ESはMapR-Data Platformを実現するために同時に実行され、分散され、スケールし、信頼性があり、高い性能を持つクラスタのすべてのワークロードをサポートします。
本記事ではアーキテクチャの詳細について紹介しましたが、MapR-DBを選ぶべきすべての理由を紹介しきれていません。
以下のリンクではより多くの内容を紹介しておりますので、御覧ください。
- MapR-DB 6.0 - The Modern Database for Global Data-Intensive Applications
- MapR-DB Product page
- Analyzing the Performance of MapR-DB, a NoSQL Database in the MapR Data Platform
- MapR-DB documentation
- An In-Depth Look at the HBase Architecture
- Architecture Matters for Production Success
- Free ODT: MapR Data Platform Essentials
Cassandraのリペアやメンテナンスに関するその他の情報については以下のリンクをどうぞ