このブログ投稿は、Clouderaとの統合前にHortonworks.comで公開されていました。一部のリンク、リソース、または参照は、正確でなくなる可能性があります。
この投稿では、HBaseのコア領域の1つを技術的に深く掘り下げます。具体的には、Apache HBaseがリージョン全体に負荷を分散し、リージョン分割を管理する方法を見ていきます。 HBaseは、データの行をテーブルに格納します。テーブルは「リージョン」と呼ばれる行のチャンクに分割されます。これらのリージョンはクラスター全体に分散され、RegionServerプロセスによってホストされ、クライアントプロセスで使用できるようになります。リージョンはキースペース内の連続した範囲です。つまり、リージョンの開始キーと終了キーの間でソートされるテーブル内のすべての行が同じリージョンに格納されます。リージョンは重複していません。つまり、単一の行キーは、任意の時点で正確に1つのリージョンに属します。リージョンは、任意の時点で単一のリージョンサーバーによってのみ提供されます。これにより、HBaseは単一の行内で強力な一貫性を保証します#。 -ROOT-および.METAと一緒に。リージョン、テーブルのリージョンは、テーブル内の行を見つけるために3レベルのBツリーを効果的に形成します。
リージョンは、列ファミリーに対応する多くの「ストア」で構成されます。ストアには、1つのmemstoreと0個以上のストアファイルが含まれます。各列ファミリーのデータは個別に保存され、アクセスされます。
テーブルは通常、多くのリージョンで構成され、リージョンサーバーは多くのリージョンサーバーによってホストされます。したがって、リージョンは、書き込みとクエリの負荷をリージョンサーバー間で分散するために使用される物理メカニズムです。テーブルが最初に作成されるとき、HBaseはデフォルトで、テーブルに1つのリージョンのみを割り当てます。これは、リージョンサーバーの数に関係なく、最初はすべてのリクエストが単一のリージョンサーバーに送信されることを意味します。これが、空のテーブルにデータをロードする初期段階でクラスターの全容量を利用できない主な理由です。
事前分割
HBaseがテーブルに対して領域を1つだけ作成する理由は、行キースペース内に分割点を作成する方法をHBaseが認識できない可能性があるためです。このような決定は、データ内のキーの分布に大きく依存します。 HBaseは、推測して結果に対処するのではなく、クライアントからこれを管理するためのツールを提供します。事前分割と呼ばれるプロセスを使用すると、テーブルの作成時に分割ポイントを指定することで、多くの領域を持つテーブルを作成できます。事前分割により、初期負荷がクラスター全体でより均等に分散されるようになるため、キーの分散を事前に知っている場合は、常にそれを使用することを検討する必要があります。ただし、事前分割には、データの偏りのため、または非常にホットな行や大きな行が存在する場合に、負荷を実際に均等に分散しない領域が作成されるリスクもあります。領域分割ポイントの初期セットの選択が不十分な場合、負荷分散が不均一になり、クラスターのパフォーマンスが制限される可能性があります。
特定の負荷に対する最適なリージョン数についての簡単な答えはありませんが、分割数としてリージョンサーバーの数の低い倍数から始めて、残りを自動分割に任せることができます。
事前分割に関する1つの問題は、テーブルの分割点を計算することです。 RegionSplitterユーティリティを使用できます。 RegionSplitterは、プラグ可能なSplitAlgorithmを使用して、分割点を作成します。 HexStringSplitとUniformSplitは、2つの事前定義されたアルゴリズムです。前者は、行キーに16進文字列のプレフィックスがある場合に使用できます(プレフィックスとしてハッシュを使用している場合など)。後者は、ランダムなバイト配列であると仮定して、キースペースを均等に分割します。カスタムSplitAlgorithmを実装して、RegionSplitterユーティリティから使用することもできます。
$ hbase org.apache.hadoop.hbase.util.RegionSplitter test_table HexStringSplit -c 10 -f f1
ここで、-c 10は、要求された領域の数を10として指定し、-fは、テーブルに必要な列ファミリーを「:」で区切って指定します。このツールは、10個のリージョンを持つ「test_table」という名前のテーブルを作成します。
13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,,1358563771069.acc1ad1b7962564fc3a43e5907e8db33.', STARTKEY => '', ENDKEY => '19999999', ENCODED => acc1ad1b7962564fc3a43e5907e8db33,} 13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,19999999,1358563771096.37ec12df6bd0078f5573565af415c91b.', STARTKEY => '19999999', ENDKEY => '33333332', ENCODED => 37ec12df6bd0078f5573565af415c91b,} ...
手元に分割ポイントがある場合は、HBaseシェルを使用して、目的の分割ポイントでテーブルを作成することもできます。
hbase(main):015:0> create 'test_table', 'f1', SPLITS=> ['a', 'b', 'c']
または
$ echo -e "anbnc" >/tmp/splits hbase(main):015:0> create 'test_table', 'f1', SPLITSFILE=>'/tmp/splits'
最適な負荷分散を実現するには、データモデルと、正しい分割アルゴリズムまたは分割ポイントを選択するためのキー分散について検討する必要があります。事前に決定された数のリージョンでテーブルを作成するために選択した方法に関係なく、データのテーブルへのロードを開始して、ロードがクラスター全体に分散されていることを確認できます。データの取り込みが開始されると、自動分割に引き継がれ、テーブルのリージョンの総数を継続的に監視できます。
自動分割
事前分割を使用するかどうかに関係なく、リージョンが特定の制限に達すると、自動的に2つのリージョンに分割されます。 HBase 0.94(HDP-1.2に付属)を使用している場合は、HBaseがリージョンの分割を決定するタイミングと、プラグイン可能なRegionSplitPolicyAPIを介して分割ポイントを計算する方法を構成できます。事前定義されたリージョン分割ポリシーがいくつかあります。ConstantSizeRegionSplitPolicy、IncreasingToUpperBoundRegionSplitPolicy、およびKeyPrefixRegionSplitPolicyです。
最初のものは、0.94より前のHBaseバージョンのデフォルトで唯一の分割ポリシーです。リージョン内のストアの1つ(列ファミリーに対応)の合計データサイズが、デフォルト値が10GBの構成済みの「hbase.hregion.max.filesize」よりも大きくなると、リージョンが分割されます。この分割ポリシーは、事前分割が完了していて、リージョンサーバーあたりのリージョン数を減らすことに関心がある場合に最適です。
HBase 0.94とトランクのデフォルトの分割ポリシーはIncreasingToUpperBoundRegionSplitPolicyです。これは、同じリージョンサーバーでホストされているリージョンの数に基づいてより積極的な分割を行います。分割ポリシーは、最小値に基づく最大ストアファイルサイズ(R ^ 2 *“ hbase.hregion.memstore.flush.size”、“ hbase.hregion.max.filesize”)を使用します。ここで、Rは同じリージョンの数です。同じregionserverでホストされているテーブル。したがって、たとえば、デフォルトのmemstoreフラッシュサイズが128MBで、デフォルトの最大ストアサイズが10GBの場合、リージョンサーバーの最初のリージョンは128MBの最初のフラッシュの直後に分割されます。リージョンサーバーでホストされるリージョンの数が増えると、512MB、1152MB、2GB、3.2GB、4.6GB、6.2GBなどの分割サイズが使用されます。9つのリージョンに到達すると、分割サイズは構成された「hbase」を超えます。 .hregion.max.filesize」、その時点で、10GBの分割サイズが使用されます。これらのアルゴリズムの両方で、分割がいつ発生するかに関係なく、使用される分割ポイントは、最大ストア内の最大ストアファイルの「ブロックインデックス」の中点に対応する行キーです。
KeyPrefixRegionSplitPolicyは、HBaseアーセナルへの興味深い追加です。行キーをグループ化するためにプレフィックスの長さを構成できます。この分割ポリシーにより、同じプレフィックスを持つ行のグループの途中でリージョンが分割されないようにします。キーにプレフィックスを設定している場合は、この分割ポリシーを使用して、同じ行キープレフィックスを持つ行が常に同じリージョンに配置されるようにすることができます。このレコードのグループ化は、「エンティティグループ」または「行グループ」と呼ばれることもあります。これは、アプリケーション設計で「ローカルトランザクション」(代替リンク)機能の使用を検討する際の重要な機能です。
構成「hbase.regionserver.region.split.policy」を設定するか、テーブル記述子を構成することにより、使用するデフォルトの分割ポリシーを構成できます。勇敢な人のために、独自のカスタム分割ポリシーを実装し、テーブルの作成時に、または既存のテーブルを変更することで、それをプラグインすることもできます。
HTableDescriptor tableDesc = new HTableDescriptor("example-table"); tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, AwesomeSplitPolicy.class.getName()); //add columns etc admin.createTable(tableDesc);
事前分割を行っていて、リージョン分割を手動で管理する場合は、「hbase.hregion.max.filesize」を高い数値に設定し、分割ポリシーをConstantSizeRegionSplitPolicyに設定することで、リージョン分割を無効にすることもできます。ただし、リージョンがリージョンサーバーの機能を超えて拡大しないように、100GBなどのセーフガード値を使用する必要があります。たとえば、キープレフィックスに均一なハッシュを使用している場合は、自動分割を無効にすることを検討し、領域の初期セットを事前分割に依存することができます。また、各領域への読み取り/書き込みの負荷とそのサイズを確認できます。表の領域全体で均一です。
強制分割
HBaseを使用すると、クライアントはクライアント側からオンラインテーブルを強制的に分割することもできます。たとえば、HBaseシェルを使用して、テーブルのすべての領域を分割したり、オプションで分割点を指定して領域を分割したりできます。
hbase(main):024:0> split 'b07d0034cbe72cb040ae9cf66300a10c', 'b' 0 row(s) in 0.1620 seconds
HBaseの負荷分散を注意深く監視し、一部の領域に不均一な負荷がかかっていることがわかった場合は、それらの領域を手動で分割して負荷を均等にし、スループットを向上させることを検討してください。手動分割を実行するもう1つの理由は、領域の最初の分割が最適ではないことが判明し、自動分割を無効にした場合です。これは、たとえば、データの分布が時間の経過とともに変化する場合に発生する可能性があります。
リージョン分割の実装方法
書き込み要求はリージョンサーバーによって処理されるため、「memstore」と呼ばれるメモリ内ストレージシステムに蓄積されます。 memstoreがいっぱいになると、そのコンテンツは追加のストアファイルとしてディスクに書き込まれます。このイベントは「memstoreflush」と呼ばれます。ストアファイルが蓄積されると、RegionServerはそれらを結合されたより大きなファイルに「圧縮」します。各フラッシュまたは圧縮が終了した後、RegionSplitPolicyが領域を2つに分割する必要があると判断した場合、領域分割要求がキューに入れられます。 HBase内のすべてのデータファイルは不変であるため、分割が発生した場合、新しく作成されたドーターリージョンはすべてのデータを新しいファイルに再書き込みしません。代わりに、参照ファイルという名前のファイルのような小さなシンボリックリンクを作成します。これは、分割点に応じて親ストアファイルの上部または下部を指します。参照ファイルは通常のデータファイルと同じように使用されますが、レコードの半分だけが使用されます。親領域の不変データファイルへの参照がなくなった場合にのみ、領域を分割できます。これらの参照ファイルは圧縮によって徐々にクリーンアップされるため、リージョンはその親ファイルの参照を停止し、さらに分割できます。
リージョンの分割はRegionServerで行われるローカルの決定ですが、分割プロセス自体は多くのアクターと調整する必要があります。 RegionServerは、分割の前後にマスターに通知し、.METAを更新します。クライアントが新しいドーターリージョンを検出できるようにテーブルを作成し、HDFSのディレクトリ構造とデータファイルを再配置します。分割はマルチタスクプロセスです。エラーが発生した場合にロールバックを有効にするために、RegionServerは実行状態に関するメモリ内ジャーナルを保持します。分割を実行するためにRegionServerが実行するステップは、図1に示されています。各ステップには、ステップ番号のラベルが付いています。 RegionServersまたはMasterからのアクションは赤で表示され、クライアントからのアクションは緑で表示されます。
1. RegionServerは、リージョンを分割することをローカルで決定し、分割を準備します。最初のステップとして、SPLITTING状態の/ hbase / region-in-transition/region-nameの下のzookeeperにznodeを作成します。
2。マスターは、移行中の親領域znodeのウォッチャーを持っているため、このznodeについて学習します。
3。 RegionServerは、HDFSの親のリージョンディレクトリの下に「.splits」という名前のサブディレクトリを作成します。
4。 RegionServerは親リージョンを閉じ、キャッシュのフラッシュを強制し、ローカルデータ構造でリージョンをオフラインとしてマークします。この時点で、親リージョンに着信するクライアント要求はNotServingRegionExceptionをスローします。クライアントはバックオフを使用して再試行します。
5。 RegionServerは、娘領域AおよびBの.splitsディレクトリの下に領域ディレクトリを作成し、必要なデータ構造を作成します。次に、親領域のストアファイルごとに2つの参照ファイルを作成するという意味で、ストアファイルを分割します。これらの参照ファイルは、親リージョンファイルを指します。
6。 RegionServerは、HDFSに実際のリージョンディレクトリを作成し、各娘の参照ファイルを移動します。
7。 RegionServerはPutリクエストを.METAに送信します。テーブルを作成し、.METAで親をオフラインとして設定します。テーブルと娘の地域に関する情報を追加します。この時点では、.METAに個別のエントリはありません。娘のために。クライアントは、.META。をスキャンすると親領域が分割されていることを確認できますが、.META。に表示されるまで、娘についてはわかりません。また、これを.METAに設定した場合。成功すると、親は効果的に分割されます。このRPCが成功する前にRegionServerが失敗した場合、マスターとリージョンを開く次のリージョンサーバーは、リージョン分割に関するダーティ状態をクリーンアップします。 .METAの後。ただし、更新すると、リージョン分割はマスターによってロールフォワードされます。
8。 RegionServerは、書き込みを受け入れるために娘を並行して開きます。
9。 RegionServerは、娘AとBを.METAに追加します。地域をホストしているという情報と一緒に。この時点以降、クライアントは新しいリージョンを検出し、新しいリージョンにリクエストを発行できます。クライアントは.METAをキャッシュします。エントリはローカルにありますが、リージョンサーバーまたは.META。にリクエストを送信すると、キャッシュが無効になり、.META ..
10から新しいリージョンについて学習します。 RegionServerは、zookeeperのznode / hbase / region-in-transition / region-nameを更新してSPLIT状態にし、マスターがそれについて学習できるようにします。バランサーは、必要に応じて、ドーターリージョンを他のリージョンサーバーに自由に再割り当てできます。
11。分割後も、メタおよびHDFSには親領域への参照が含まれます。これらの参照は、ドーター領域での圧縮によってデータファイルが書き換えられるときに削除されます。マスターのガベージコレクションタスクは、娘領域がまだ親ファイルを参照しているかどうかを定期的にチェックします。そうでない場合、親領域は削除されます。
リージョンのマージ
リージョン分割とは異なり、現時点ではHBaseはリージョンをマージするための使用可能なツールを提供していません。 HMergeおよびMergeツールがありますが、それらは一般的な使用にはあまり適していません。現在、オンラインテーブルと自動マージ機能はサポートされていません。ただし、OnlineMerge、マスターが開始する自動リージョンマージ、テーブル操作用のZKベースの読み取り/書き込みロックなどの問題により、リージョン分割を安定させ、リージョンマージのサポートを改善するよう取り組んでいます。しばらくお待ちください!
結論
ご覧のとおり、内部のHBaseは、リージョンの分割を管理し、リージョン全体で自動シャーディングを実行するために、多くのハウスキーピングを実行します。ただし、HBaseはリージョン管理に必要なツールも提供しているため、分割プロセスを管理できます。また、RegionSplitPolicyを使用して、リージョン分割がいつどのように発生するかを正確に制御することもできます。
テーブル内のリージョンの数、およびそれらのリージョンがどのように分割されるかは、HBaseクラスターの負荷を理解および調整する上で重要な要素です。キーの分布を見積もることができる場合は、最適な初期ロードパフォーマンスを取得するために、事前分割を使用してテーブルを作成する必要があります。最初のリージョン数の開始点として、リージョンサーバーの数の少ない倍数から始めて、自動分割に引き継ぐことができます。初期分割ポイントを正しく見積もることができない場合は、1つの領域でテーブルを作成し、自動分割で初期ロードを開始し、IncreasingToUpperBoundRegionSplitPolicyを使用することをお勧めします。ただし、リージョンの総数は時間の経過とともに安定し、現在のリージョン分割ポイントのセットは、テーブルがこれまでに受信したデータから決定されることに注意してください。リージョン全体の負荷分散を常に監視することをお勧めします。負荷分散が時間の経過とともに変化する場合は、手動分割を使用するか、より積極的な領域分割サイズを設定してください。最後に、今後のオンラインマージ機能を試して、ユースケースに貢献することができます。