pandazx's blog

データ分析など雑多な技術ブログ

HBaseのデータ書き込みの仕組み

以下の記事の意訳と補足
Apache HBase Write Path | Apache Hadoop for the Enterprise | Cloudera

本記事ではHBaseがどうやってHadoop上で低レイテンシな読み書きを実現しているか、また、データの更新が出来ないHadoopでどうやって更新を実現しているかを説明している。

ポイントは

  • HBaseのデータはrow keyのソート制約があること
  • ディスク上のHFile、メモリ上のMemstore、データロストを回避するためのWAL


以下、内容。


本記事のwrite pathとは、データのputやdeleteを行う方法を意味する。
このpathはクライアントで始まり、region serverに行き、データがHFileにeventuallyに書き込まれて終わる。

write pathにはregion serverと通信できない状態でもデータがロストしない仕組みがある。

HBaseの各テーブルは以下の3つのカテゴリから成るサーバ群によって構成される。

  • master serverが一つ
  • 一つ以上のbackup master servers
  • 多数のregion servers

HBaseテーブルは巨大になっても、パーティションで分割されてregionという単位になり、複数のregion serverで管理される。

ちなみに、masterはデータを持たないので、クラッシュしてもデータをロストしない。

クライアントからの更新リクエストの扱い

HBaseのデータはソートされたmapで構成される。そのソートされたmapのkeyでパーティションされたものがregionである。クライアントがデータの変更をリクエストした時、デフォルトでは、すぐにregion serverにルーティングされる。ただ、autoflashをoffにすることで、クライアントでその変更リクエストを溜めておくこともでき、バッチ的に変更リクエストをまとめてregion serverに送れる。

autoflush がoffの場合、flush-commitsが実行されるまでか、バッファがいっぱいになるまで溜められる。バッファサイズはプログラムで指定できる。指定しなければ、hbase.client.write.bufferで設定される。

HBaseのデータはrow keyでソートされて保存される仕組みになっているので、あるkeyがどのregion serverで管理されるかは簡単にわかるようになっている。そのため、クライアントは適切なregion serverにアクセスできる。

クライアントがregion serverのアドレスを取得する手順を説明する。最初に、ZooKeeper quorumから-ROOT- region のアドレスを取得する。次にregion serverのアドレスを管理している-META- regionのアドレスを取得する。次にクライアントは目的のregion serverにアクセスできる。

クライアントは毎回、上記処理を行うのをスキップするため、region serverのアドレスをキャッシュできる。アドレスが変更されたり、エラーになった場合はキャッシュがアップデートされる。

region serverがクライアントからpur/deleteの更新リクエストを受信しても、すぐにはHFileに反映しない。これはrow keyのソート状態を維持するため。

これがランダムなrowを効率よく検索するのを可能にする(ソートされてるからキーでの検索が効率的に行えるという意味?)。HFileのソートを保つため、データをHFileにランダムにインサートできないので、代わりに新しいHFileに書き込むことになる。もし更新が個別のファイルに書き込まれる場合、大量の小さなファイルができてしまう。これではHBaseがスケーラブルにならない。そのため、更新はすぐにHFileに反映されないようにしている。

Memstore

代わりに、更新情報は一旦、memstoreと呼ばれるメモリ上に保存される。memstoreは軽量で効率的なランダム書き込みをサポートする。

memstore上のデータはHFileと同様にソートされる。細かく書き込むよりも、大きな単位でデータを書き込む方がHDFSの利点を活かせるので、memstoreに十分にデータが溜まった段階でHDFS上のHFileにデータを書き込む。

memstoreを使うことでパフォーマンスを向上できるが、メモリ上でデータを保持するため、サーバがfailすると、データロストのリスクがある。

WAL(write-ahead-log)

このリスクがあるため、HBaseはmemstoreより前にWALファイルに更新情報を保存する。これにより、仮にregion serverがfailしてもWALからリカバリできる。

注意:デフォルトではWALは有効になっている。 しかし、WALに書くということはリソースを消費するということでもある。WALを無効にすることもできるが、その場合はデータロストのリスクを負うことになるので、その対策をユーザ自身が行う必要が出てくる。

WALファイルのデータ構成はHFileとは異なる。WALファイルは更新情報(edit)のリストを含み、各editはputやdeleteで表現される。editは更新情報と対象regionの情報を含む。editはWALファイルの末尾に追加されるので時間順に並ぶ。つまり、書きこむ際にファイル内にランダムアクセスする必要はない。また、ディスクに保存されるのでmemstoreと違って永続的である。

WALファイルが大きくなると新しいWALファイルが作られる。これを「WALファイルをrollingする」と言う。rollingされたWALファイルにはそれ以降、新しいeditは追加されなくなる。

デフォルトでは、WALファイルの大きさがHDFS block sizeの95%になったらrollingされる。これは「hbase.regionserver.logroll.multiplier」で設定できる。「hbase.regionserver.hlog.blocksize」でブロックサイズを変えてもよい。

rollingは定期的にも実行される。「hbase.regionserver.logroll.period」でインターバルを設定できる。デフォルトでは1時間。これが実行されると、上記のサイズとは関係なく、rollingされる。

WALファイルをrollingしておくことはリカバリが実行される時に役に立つ。なぜならば、リカバリ対象となるregionはその間、利用できなくなるため。リカバリが終わるとWALファイルはアーカイブされ、後でLogCleanerデーモンによって削除される。

WALファイルはregion serverがクラッシュしてデータをロストした場合のみリカバリに利用される。

region serverは複数のregionを保持するが、各regionに対応するWALファイルは持たない。同じサーバで保持したのではクラッシュした場合にリカバリできないから。代わりに、すべてのregionをカバーする1つのWALファイルをregion server間で共有する。WALファイルは定期的にrollingされるので、各region serverは複数のWALファイルを持つことになる。しかし、ある時点でアクティブなWALファイルはregion serverごとに1つだけである。

HBaseのrootパスを/hbaseとすると、あるregion serverのすべてのWALはrootフォルダの下に以下の書式のパスで保存される。

/hbase/.logs/<host>,<port>,<startcode>

例)

/hbase/.logs/srv.example.com,60020,1254173957298

WALのファイル名の書式は以下のようになる。

/hbase/.logs/<host>,<port>,<startcode>/<host>%2C<port>%2C<startcode>.<timestamp>

例)

/hbase/.logs/srv.example.com,60020,1254173957298/srv.example.com%2C60020%2C1254173957298.1254173957495

WALファイルの各editはユニークなシーケンスIDを持つ。このIDはeditの順番を保つために大きくなっていく。いつWALファイルがrollingされても、次に振られるシーケンスIDとrolling前のファイル名はメモリのmapに保存される。この情報は、memstoreがディスクにフラッシュされる時に後でWALファイルがアーカイブできるかどうか判断するのを容易にするために、各WALファイルの最大シーケンスIDをtrack(追跡)するために使われる(ちょっと意味がわからない)
This information is used to track the maximum sequence id of each WAL file so that we can easily figure out if a file can be archived at a later time when some memstore is flushed to disk.


editとそれらのシーケンスIDはregion単位の中ではユニークである。WALファイルにeditが追加されたら、editのシーケンスIDも最新のIDとして記録される。memstoreがディスクにフラッシュされる時に、最新のシーケンスIDはクリアされる。もし、ディスクに書かれたシーケンスIDがWALファイルの最大シーケンスIDと同じだったら、このregionのeditがすべてディスクに書きこまれたと判断できる。もしWALファイルにあるすべてのregionのすべてのeditがディスクに書きこまれたら、splitまたはリカバリされるデータは無いことを意味する。そして、WALファイルはアーカイブされる。

WALファイルをrollingする、memstoreをフラッシュするという2つのアクションは一緒に起こってはいけない。しかし、region serverごとに多すぎるWALファイルはリカバリ時間が長くなるので保持したくない。そのため、WALファイルがrollingされたら、HBaseはWALファイルが多すぎないかチェックする。そして、フラッシュされるべきか判断する。

まとめ

この記事では、HBaseがデータをどうやって作成、更新しているか理解するために、write pathについて説明した。

以下の点がポイントである。

  • クライアントはregion serverをどう把握するか
  • 高速なランダムwriteをサポートするMemstore
  • region serverクラッシュ時のデータロストを回避するためのWALファイル

今後のブログでは、HFile formatやWALファイルのsplitなどについて説明する予定だ。

コメント欄にはMemstoreについてより詳細が述べられている記事が紹介されていた。
Configuring HBase Memstore: What You Should Know « Sematext Blog