HBase ACID semantics

HBaseのクライアントが書き込みを行った時に、読み込み可能になるのはいつなのかというのが気になってきたので、調べてたらApache HBase – Apache HBase (TM) ACID Propertiesにバッチリなページを発見したので読んでみることにした。

ACID定義
  • Atomicity:オペレーションは、全て完了するか、全く完了しないかのどちらかになる。
  • Consistency:全てのアクションによって、妥当なテーブル状態から別の妥当なテーブルの状態に遷移する。例えば、更新の途中状態が見えたりはしない。
  • Isolation:並列で実行中のトランザクションは、お互い影響せず独立しているように見えること。
  • Durability:クライアントに成功を返却した更新データは、喪失しない。
  • Visibility:更新が完了したデータは、続いて読み込みをかけたときに、その更新がreaderに見えるようになった時点で、visibleになるという。
may mustの定義

mayとmust はRFC2119に定義される

  • may:現在のリリースでの提供が保証されているとしても、それに頼ることはすべきでない。
  • must:もしも、その文章の例外が一つでもある場合には、それはバグであることを意味する。
対象とするAPI
  • Read APIs
    • get
    • scan
  • Write APIs
    • put
    • batch put
    • delete
  • Combination (read-modify-write) APIs
    • incrementColumnValue
    • checkAndPut
ACID 観点での説明
  • Atomic
    1. 全てのmutations(書き込みの単位)は、行単位でアトミック。どんなputも完全に成功するか、何も変更せずに失敗するかのどちらか。
      1. 成功というコードを返却したなら、完全に成功している。
      2. 失敗というコードを返却したなら、完全に失敗している。
      3. タイムアウトが返却されれた倍は、成功しているかもしれないし、失敗しているかもしれない。ただし、部分的に成功していることはない。
    2. mutationが複数のカラムファミリに対する更新であったとしても、ひとつのrowにおいては、ここまで記述したことは保証される。
    3. 複数行にわたる更新を行うAPIは、アトミック性は保証されない。例えば、'a','b','c'というrowに対して、multiputを行った場合、1部分のrowのみ更新され、残りは更新されてない状態で返却されるかもしれない。そのような場合、APIは返却コードのリストを返却し、それらは、成功、失敗、タイムアウトのいずれかになる。
    4. checkAndPut APIは、いわゆるCASを提供する。
    5. 複数の更新があった時の順番は、rowごとに明確に定義されていて、交わったりしない。例えば、(col1=1,col2=1,col3=1)と(col1=2,col2=2,col3=2)という更新要求二つを投げたときには、いずれかの更新のみが適用される。(col1=1,col2=2,col3=1)となったりすることはない。
      1. multirow batch mutationsに関しては、例外であることに注意すること。(mutirow でbatchの場合、なぜ交じるということになるんだろう?よくわからない。)
  • Consistency and Isolation
    1. 任意のAPIを通して返却された全てのrowは、テーブルヒストリにおけるある時点に存在した完全な行を返却する(いわゆる、Snapshot Isolationのことと思う。内部的にrevisionを持っていて、Queryを投げるときには、revisionを指定すれば良いと思ったら、scanは違うと下の方で明記)
    2. カラムファミリをまたがったとしても、保証される。更新1,2,3,4,5が並行して要求されているときに、getを要求した場合、各更新のなかでどれかが返却される。この際、複数更新のカラムファミリが交じることはない。
    3. rowの状態は、それに対する書き込み履歴とともに進むでいくだけである(ロールバックとかないよと言いたいんだと思うけど、書き方がわかりずらいなあ)
  • Consistency of Scans

scanは、テーブルの一貫性を保証したviewではない。snapshot isolationは、提供しない。scanが提供するは、以下のようである。

    1. scanによって返却された任意のrowは、一貫性を持っている。つまり、ある時点での完全なrowを返却している。
    2. scanは、少なくともscanを始めた時刻よりも新しいデータを返却する。これは、次に列挙する、visibilityの保証を満たす。
      1. 例えば、クライアントAがデータXを更新し、クライアントBに連絡、クライアントBがフルスキャンをかけたら、少なくともデータXは取得される。
      2. scanは、scannerが構築される前までにコミットされた全ての更新を結果に含まなければならない、そして、もしかするとscannerが作成されたあとコミットされたデータも含むかもしれない。

これら特徴は、RDBで言うところの、read committedに相当すると理解できるだろう。気をつけて欲しいのは、scannerの一貫性に言及するときには、あくまでトランザクションのコミット時間を対象にしているのであって、セルのタイムスタンプのことではないことである。つまり、ある時刻tに、tより大きい値のタイムスタンプを持つデータを取得するかもしれないが、それは、そのような未来のタイムスタンプを持つデータで更新したためだ。

  • Visibility
    1. 任意の更新に対して、成功をクライアントが受け取ったときには、直ちにその更新は全てのクライアントから参照可能となる。
    2. rowは、いわゆるタイムトラベルのような現象を見せてはならない。つまり、更新履歴によりrowは状態遷移することになるが、その更新と並行に要求される読み込みはそれらの状態を部分列を返却する
      1. 例えば、rowのcellがincrementColumnValueAPIを用いて更新されたなら、クライアントは、決してcellの値が減少したことを見てはいけない。
      2. リード要求に対して返却されたcellの任意のバージョンは、永続的に保存されていることが保証されている。
  • Durability
    1. 全ての参照可能なデータは、永続化されてる。ディスクに永続化されていないデータは、参照できない。
    2. 成功が返却された任意の要求は、永続化されている
    3. 失敗が返却されたなら、永続化されていない。これは、Atomic性を保証するための制限でもある。
    4. 全ての合理的な(予想できないものではないという意味だと思う)失敗をしたとしても、このドキュメントに書いてあることは保証される。
  • Tunability

ここまでで書いてきた保証は、HBaseにおいて達成しなければならない。パフォーマンスに対するトレードオフを考えたいユーザに対して、いくつかのチューニングオプションを提供している。

    • リード要求における紛失やタイムトラベルを許すために、"per-read basis" に対してVisibilityをチューンしてよい。(よくわからない。)
    • 永続性を以下のようにチューンして良い。一定期間が過ぎたらディスクへflush。

デフォルトではどうなっているんだろうか。最後のチューンの部分。私の理解だと、putをしてもクライアント側のバッファに貯めこむだけだった気がするんだが。Apache HBase ™ Reference Guideを観てみてみると、以下の記述がある。

8.3.2. WriteBuffer and Batch Methods
もし、AutoFlush機能がオフにしていると、Putsは、ライトバッファがいっぱいになるとRegionServersにsendする。デフォルトでライトバッファは2MBで、HTableインスタンスを破棄する前に、close()かflushCommits()をしないと、このバッファにあるデータが失われる。htable.delete()は、ライトバッファには書き出されない。

だよね、こんな感じだったと思った。デフォルトで、AutoFlushはオンらしいので、基本的にはflushしてくれるわけですね。