Compactionについて
Flushが何度も行われると、HFileの数が増加していき、読み込みが非効率になっていきます。そこでHBaseは、Compactionという複数のHFileをまとめて1つのHFileにするという処理を行います。
Compactionは、メジャーとマイナーの2つの種類があります。
- マイナーCompaction:新しいいくつかのHFileを1つのHFileにまとめる処理をします
- メジャーCompaction:すべてのHFileを1つのHFileにまとめる処理をします
メジャーCompactionの際には、不必要なバージョンを削除したり、前述した削除マーカーとともに実際のデータも消します。
また、HBaseでは、ColumnFamily内のValueに対してTTL(Time to Live,生存期間)を設定することができますが、メジャーCompactionの際にはTTLが過ぎたデータも削除します。
以下、Compactionのイメージ図になります。
HFileはすでにRowKey+ColumnFamily+Column+Timestampでソートされているので、実際にマージする際には、各HFileを先頭から読み出しながら行います。
Splitについて
特定Regionのデータ量が増えていくと、Region間でのデータ量やアクセスの偏りが発生する可能性があります。この偏りを防ぐために、HBaseは大きくなったRegionの分割を行います。これをSplitと呼びます。
各HRegionServerで担当Regionのサイズをモニタリングし、ある一定以上のサイズになるとそのRegionをちょうど半分に分割します。
これにより、各HRegionServer間でRegion数に差がでた際に、Balancerにより多くRegionを割り当てられているHRegionServerから、別のHRegionServerにRegionを割り当て直します。
このようにすることで、各HRegionServer間でのデータ量やアクセスを均一に保とうとしています。
HBaseの読み込みフロー
それでは、HBaseの読み込みについて見ていきましょう。
書き込みフローで説明した通り、データはまずメモリ上のMemStoreに書き込まれ、一定サイズを超えるとHFileとしてFlushされるということを繰り返します。つまり、読み込みたいデータはMemStore、あるいは複数あるHFileのどこにあるのか分からないということになります。
そこで、HBaseにおけるデータの読み込み時には、基本的にすべてのHFileをすべて読み込む必要(※1)があります。HBaseは書き込みが速い分、読み込みが多少遅くなるアーキテクチャになっています。
以下、読み込み時の簡単なイメージ図になります。
MemStoreやHFileは、RowKey+ColumnFamily+Column+Timestampでソートされているので、MemStoreや各HFileの先頭から読み出していき必要なエントリーだけを取り出していくという処理になります(※2)。
上のイメージ図の例では、startRowは"row2"、stopRowは"row4"(stopRowは含まれない)の範囲で、かつVersionが1(最新のみ)でscanしています。
MemStoreや各HFileの先頭から読み出していき、startRowかもしくはそれより大きいRowKeyのRowを探索し、それぞれのデータを比べ最新のものだけを返しています。そして、stopRowもしくはそれより大きいRowKeyのRowまで進んだ所で探索を終了します。
実際には、Bloom Filterやタイムスタンプを用いて読み込む必要のあるHFileを選別することができます。また、Block Cacheという機構を使ってキャッシュをしているので、毎回HFileにアクセスしているわけではありません。
なぜ、HBaseはこのような構造になっているのでしょうか。
HBaseのベースとなっている考え方にLSM-tree(Log-Structured Merge-tree)があります。
前提として、大規模なデータを扱う場合にはディスクの転送に占める時間はかなり大きいものになります。
一般的なRDBではB+treeがよく使われていますが、ディスクのシークがLSM-treeに比べて多く発生します。対して、LSM-treeではシークがあまり発生しないので、ディスクの転送量をフルに使って処理を行うことができます。
大規模環境では、後者の方が効率的なので、HBaseでは後者のアプローチをとっています。
HBaseの耐障害性
ここまで、WALやHFileの書き込みは"ファイルシステムへ"という表現をしてきました。
HBase自体にはデータの耐障害性に関する機能はなく、下位のファイルシステムに依存しています。下位のファイルシステムには通常はHadoop分散ファイルシステム(以下、HDFS)を使用します。
HDFSについては、本連載から外れた内容のため詳しい説明はしませんが、デフォルトで3つのレプリカを持つことでデータの耐障害性を高めています。
各HRegionServerからHDFSに書き込まれたWALやHFileは、HMasterや他のHRegionServerからも読み込むことができるので、あるHRegionServerがダウンしたとしても他のHRegionServerにフェイルオーバできます。
また、HDFSではデータのローカリティも考慮されています。各HRegionServerで書き込まれたデータのレプリカの一つは必ずローカルにあるので、ネットワークを介さずにアクセスできます。
まとめ
今回は、HBaseのアーキテクチャについて解説しました。次回からは、実際にHBaseを使ってアプリケーションを作っていきます。
また、株式会社サイバーエージェントでは、Hadoop/HBaseエンジニアを募集しています。ご興味のある方はこちらからエントリーしていただければと思います。エンジニア>R&Dエンジニア>R&Dエンジニアを選択しエントリーしていただければ幸いです。
参考資料
- Apache HBase Home
- 「Bigtable: A Distributed Storage System for Structured Data」
- 『HBase』 Lars George 著、玉川竜司 訳、オライリージャパン、2012年7月


