Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

HBaseのcompactionについて調べてみた(1)

cw-sakamotoです。 今までNikeのランニングシューズ(特にストリーク6)を好んでいたのですが、最近adidasのjapan boostに浮気気味です。 確かにフィット感がいいですね。sub2も興味がありますが、私の走力*1であれを履くのは恥ずかしいな、と思って、まだ試していません。 匠シリーズも気になるところですが、三村さんがいなくなった今、今後もあのシリーズが続く可能性は低い気がして、手を出していません。

閑話休題。

ChatWorkでは、HBaseを利用しています。利用用途はメッセージDBです。 もともとはAmazon Auroraを利用していましたが、負荷に耐えられないのが目に見てきたため、2016年12月にHBaseやKafkaをベースとしたアーキテクチャへの刷新を行いました。

hrnabi.com

昨年のAWS Summit TokyoでKafkaと絡めてどのように利用しているのか、の詳細がありますので、興味のある方はご参照ください。

ここ最近、HBaseのcompactionについて、調べる必要があったので、この記事ではその整理とまとめを記載したいと思います。 ただ、自分自身まだ追いきれていないところもあり、連載形式にしたいと思います。

この記事では、HBaseにおける

  • compactionの必要性
  • {major, minor} compaction のそれぞれの{動き, 発動する条件}の概要
  • compactionに関連するパラメータのまとめ

を記載します。

目次

もしこのブログを読まれた方で、間違いを見つけましたら、ぜひ指摘して頂きたいです。 HBaseのversionは、HBase 1.2.0-CDH5.9.0です。

HBaseの概要については、下記がわかりやすいと思います。

thinkit.co.jp

なぜcompactionが必要なのか

HBaseはLSM-tree構造*2を採用しており、Wikipediaで言うところの、C0にMemStore, C1にHFile(StoreFile)*3となっています。LSM-treeは、追加していくのは早いのですが、C1(HFile)がどんどん作成されていくので、マージ作業が必要となります。 そこで行われるのがcompactionで、HBaseにはmajor compactionとminor compationの2つがあります。

major compactionは、Store内の全HFileを1つのHFileにまとめるcompactionです。そのため、compaction負荷(IO負荷)が非常に大きいです(minor compactionももちろんそれなりに大きいです)。 f:id:cw-sakamoto:20180507172112p:plain

minor compactionは、複数のHFileを1つにするcompactionです。 f:id:cw-sakamoto:20180507172011p:plain

日々flushされたHFileをminor compacitonで少なめにし、major compactionで1つにまとめる、ということが基本的な動作になります。

もちろんminor compactionを挟むのではなく、いきなりmajor compactionというのも条件次第では起こりえます。

※上記の図は下記のブログの図を借用しております

https://acadgild.com/blog/hbase-compactions/

compactionが起きるのはいつか

では、compactionはいつ起きるのでしょうか。

下記のタイミングでcompactionCheckerが走り、条件に合致すると、{major, minor} compactionが行われます。

  • MemStoreがflushされたとき
    • flushされるのは下記のタイミング
      • hbase.hregion.memstore.flush.sizeに達したか(default 128MB)
      • hbase.regionserver.optionalcacheflushinterval経過したか(default 60m)
      • snapshot作成コマンドが実行されたとき
  • Regionが作成されたとき
    • Regionが作成されるのは下記のタイミング
      • RegionのHFileのサイズが、hbase.hregion.max.filesize に達した(default 10GB)
      • commandで明示的にsplitしたとき
  • checker用threadが起動から一定時間経過したとき
    • 正確には、hbase.server.thread.wakefrequency間隔(default 10s)で起動する、checker用threadが、hbase.server.compactchecker.interval.multiplier回(default 1000)起動(つまり約166m)したとき
  • hbase-shellでcompactionが実行されたとき

このときmajor compactionもしくはminor compactionが発生する可能性があります。

compactionの条件

compactionの条件ですが、選択したcompaction policyで異なります。HBase 1.2.0-CDH5.9.0では、下記のpolicyが存在します。

  • RatioBasedCompactionPolicy
  • ExploringCompactionPolicy(default)
  • FIFOCompactionPolicy
  • StripeCompactionPolicy

compactionのpolicy

上記のpolicyは正確にはminor compactionのアルゴリズムです。 それぞれのアルゴリズムについては、下記をご参照ください。

algorithm overview source memo
RatioBasedCompactionPolicy 0.94までデフォルトだったpolicy
compacion対象のhfileを左から(古い順から)比較し、compaction対象とするかどうかをする
hfilesにpolicyを適用するメソッド ドキュメントの中に例も記載されているのでわかりやすい
ExploringCompactionPolicy RatioBasedCompactionPolicy の発展*4(より少ない負荷でcompactionが行われるように)
RatioBasedCompactionPolicyは、条件に合致するものがあれば適宜minor compaciton対象となるが、こちらは対象を広げて、候補を探す
hfilesにpolicyを適用するメソッド "Keep the selection that removes most files for least size." となるようにcompaction対象を選びます(なるべく多くのファイルを、同じファイル数の場合にはcompaction後のファイルサイズが小さくなる方を選ぶ)
FIFOCompactionPolicy TTL切れのものだけを対象にする compaction対象の選別 javadoc
StripeCompactionPolicy stripe store用のcompaction policyのようだけど、
stripe storeというのがわかっていない
StripeCompactionPolicyのソース ここを見ると実際にはExploringCompactionPolicyが適用されるようだ
major compactionはない
stripeはこちら

major compactionが起きる条件

major compactionの条件は、hbaseのドキュメントにはあまり記載がないのですが、いくつかあり、ソースを見る限り、下記のいずれかに合致した場合のようです。このあたりはまだ追いきれていません。

条件1

major compactionが強制 && ユーザがhbase shellでmajor compactionを指定 && storefileすべてがcompaction対象に含められる

が1つめの条件です。ソースのここに該当。

条件2

({[major compactionが強制 && storefileすべてがcompaction対象に含められる] || 対象のstoreのmajor compactionが前回から一定時間経過]} && [対象ファイル数がhbase.hstore.compaction.max(default 10)以下])

が2つめの条件です。ソースのここに該当。

ややこしいので、[], {}, ()でわけました。

一定時間は hbase.hregion.majorcompaction(default 7days) ± hbase.hregion.majorcompaction.jitter(default 0.5 -> 3.5days) で、ソースにあるcomConf.getMaxFilesToCompact()は、hbase.hstore.compaction.maxの値が帰ってきます

条件1,2のどちらにも "storefileすべてがcompaction対象に含められる" と "major compactionが強制" が入っていますが、これはそれぞれ、ソースのisAllFilesforceMajorに対応します。

major compactionの条件でわかっていないところ

isAllFilesは、名前は単純で、値を入れるのは、candidateFiles.size() == candidateSelection.size();の結果なのですが、candidateFilescandidateSelectionがわかっていません。

candidateFilesが、store内の全HFile、candidateSelectionがstore内の全HFileからcompactionに適合しないものを落としたものであるようですが、現状では下記がわかっていません。

  • ここで、candidateSelectionが作成されるが、そこにあるfilesCompactingがわからない
    • filesCompactingは、HStore.javaで定義・作成されているが、何が入るのかがわからない

また、forceMajortrueが入るのがどのような場合なのかもわかっていません。変数名と条件1から考えるに、ユーザのコマンド指定のときにtrueが入るのが1つのパターンのようですが、条件2にも入っていて、これがいつtrueになるのかがわかりません。

ところで、hbase.hstore.compaction.maxというパラメータは、HBaseのドキュメントでは、"The maximum number of StoreFiles which will be selected for a single minor compaction"と記載されていますが、major compactionの条件でもあるので、注意です。

compactionの動きを左右するパラメータ

最後にcompactionの動きに関連するパラメータを記載したいと思います。いくつかはここまでの説明で出てきましたが、それも含めて記載します。

おもにHBase(CDH5)のドキュメントを参考にしています。

parameter role default memo
hbase.hregion.majorcompaction Time between major compactions 604800000
milliseconds (7 days)
0にすると、時間契機でのmajor compactionは
なくなる。この時間はStore毎に計測される
hbase.hregion.majorcompaction.jitter A multiplier applied to
hbase.hregion.majorcompaction
to cause compaction
0.5 7daysの場合は、3.5日
hbase.hstore.compaction.ratio minor compaciton ratio 1.2 この値を大きくすると
大きめのminor compactionが起きるようになる
hbase.offpeak.start.hour start offpeak hour -1 offpeakの設定(開始)
hbase.offpeak.end.hour end offpeak hour -1 offpeakの設定(終了)
hbase.hstore.compaction.ratio.offpeak minor compaciton ratio for
more aggressive compaction at offpeak
5 minor compactionの比率の変更
offpeak以外の時間帯はこの比率でminor compacitonの判断が行われる
hbase.hstore.compaction.min the minimum number of StoreFiles
which must be eligible for compaction
3 この値よりhfileが少ない場合には
minor compacitonは行わない
hbase.hstore.compaction.max The maximum number of StoreFiles
which will be selected for a single compaction
10 compaction対象の最大値
実はmajor compationのパラメータでもある
(前述の"major compactionが起きる条件"のところに記載)
hbase.hstore.compaction.min.size A StoreFile smaller than this size
will always be eligible for minor compaction
128MB この値より小さいStoreFileは原則compaction対象になる
hbase.hstore.compaction.max.size A StoreFile larger than this size
will be excluded from compaction
Long.MAX_VALUE この値より大きいStoreFileは原則compaction対象外になる
hbase.hstore.compaction.max.size.offpeak A StoreFile larger than this size
will be excluded from compaction at offpeak
hbase.hstore.
compaction.max.sizeと同じ
ドキュメントにはないが存在するパラメータ
hbase.hstore.compaction.max.sizeのoffpeak時の設定
hbase.hstore.min.locality.to.skip.major.compact Potentially improve block locality
during major compaction for old regions
0 major compactionをdata locality回復を
積極的に行うためのオプションだが、設定方法がいまいちわかっていない(floatで指定)
hbase.regionserver.thread.compaction.throttle Threshold for use of two threads for compaction 2 * hbase.hstore.
compaction.max
* hbase.hregion.
memstore.flush.size
hbase.hstore.compaction.max = 10
hbase.hregion.memstore.flush.size = 128M
(memstoreがflushされるサイズ)
2 * 10 * 128M = 2560M
この値を超えるcompacitonが実行される場合には、large throtleで、それ以下の場合には、small throttleでcompactionが実行される
hbase.regionserver.thread.compaction.large The number of threads to use
when the size of HFiles
to be compaction is larger than
hbase.regionserver.
thread.compaction.throttle
1 cloudera document
"The number of threads available to handle small and large compactions, respectively.
Never increase either of these options to more than 50% of the number of disks available to HBase."とあるので注意。
結局compactionとは、diskへの書込みなので、
diskの本数の50%より多くしないほうがいい、とのこと
hbase.regionserver.thread.compaction.small The number of threads to use
when the size of HFiles
to be compaction is smaller than
`hbase.regionserver.
thread.compaction.throttle
1 largeと同じ注意点がある
hbase.server.compactchecker.interval.multiplier The number that determines how often
we scan to see if compaction is necessary
1000 単位はなくて、回数。+1されていく
ソース
hbase.server.thread.wakefrequency Time to sleep in between searches
for work (in milliseconds). Used as sleep interval
by service threads such as log roller.
10 * 1000 こちらはmiliseconds。
なので、checker用のthreadは、10s間隔で起動し、さらに、1000回起動する
(10 * 1000s, 166.666..min)と、compactioncheckerが起動する。
ソース
hbase.store.delete.expired.storefile TTLが過ぎたファイルを
minor compactionで削除するかどうか
true ttlのドキュメント

最後に

HBaseのcompactionに関して、現時点で理解できている範囲でまとめてみました。 せっかくなので、今回調べて追いきれなかったところや、compactionがソースレベルでどのようなことをしているのか、など、調査を続けたいと思います。 独自にソースを読んでいったところばかりなので、間違いがあれば、ぜひぜひ指摘して頂きたいです。

弊社ではHBaseをガリガリ触りたい!という仲間を大募集しております。 corp.chatwork.com

*1:フルsub3.5程度, ハーフ95分ぐらい

*2:LSM-treeについては、下記がcassandraでの例も含めてわかりやすいです。

Log Structured Merge Tree

*3:HFileとStoreFileは同じ(HFileが慣習で、ソースはStoreFileになっている)ですが、このブログではHFileで統一して記載します。

*4:実際継承しているhbase/ExploringCompactionPolicy.java at cdh5.9.0-release · cloudera/hbase · GitHub