HBase hbckについて その1

HBaseには`hbck`という、リージョンやテーブルの整合性や完全性をチェックしたり、壊れていたりした場合に修復するツールがあります。この記事では、そのhbckの使い方や仕組みを説明したいと思います。バージョンはHBase 1.1.2 (HDP 2.5) をベースにしています。

# HBaseのメタデータと実データについて

HBaseでは、メタデータ(HBaseテーブル)と実データ(HDFS)をそれぞれ管理しています。hbckの説明に入って行く前に、HBaseではどのようにデータを保存しているのかについて見てみましょう。

## メタデータ

テーブルに関するメタデータは、HBaseのテーブルとして保存されています。そのテーブル名は`hbase:meta`です。

このテーブルに対してスキャンすると以下のようになります。

```
hbase(main):001:0> scan "hbase:meta", {STARTROW => "test"}
ROW                                                      COLUMN+CELL
 test,,1483946613382.2e2531f7c70a6fc178ac3472193ab26b.   column=info:regioninfo, timestamp=1483946614406, value={ENCODED => 2e2531f7c70a6fc178ac3472193ab26b, NAME => 'test,,1483946613382.2e2531f7c70a6fc178ac3472193ab26b.',
                                                          STARTKEY => '', ENDKEY => 'a'}
 test,,1483946613382.2e2531f7c70a6fc178ac3472193ab26b.   column=info:seqnumDuringOpen, timestamp=1483946614849, value=\x00\x00\x00\x00\x00\x00\x00\x02
 test,,1483946613382.2e2531f7c70a6fc178ac3472193ab26b.   column=info:server, timestamp=1483946614849, value=sandbox.hortonworks.com:16020
 test,,1483946613382.2e2531f7c70a6fc178ac3472193ab26b.   column=info:serverstartcode, timestamp=1483946614849, value=1483940390908
 test,a,1483946613382.0e984bc5624b8bd5d7f7e3c29fef38af.  column=info:regioninfo, timestamp=1483946614406, value={ENCODED => 0e984bc5624b8bd5d7f7e3c29fef38af, NAME => 'test,a,1483946613382.0e984bc5624b8bd5d7f7e3c29fef38af.'
                                                         , STARTKEY => 'a', ENDKEY => ''}
 test,a,1483946613382.0e984bc5624b8bd5d7f7e3c29fef38af.  column=info:seqnumDuringOpen, timestamp=1483946614858, value=\x00\x00\x00\x00\x00\x00\x00\x02
 test,a,1483946613382.0e984bc5624b8bd5d7f7e3c29fef38af.  column=info:server, timestamp=1483946614858, value=sandbox.hortonworks.com:16020
 test,a,1483946613382.0e984bc5624b8bd5d7f7e3c29fef38af.  column=info:serverstartcode, timestamp=1483946614858, value=1483940390908
2 row(s) in 0.4040 seconds
```

これはtestという名前のテーブルのメタデータになります。1つリージョンに対して1つの列がマッピングされます。今回はリージョンが2つあるので、2つの列が表示されてるのが分かります。

hbase:metaのスキーマは以下になります。

### 列キー

列キーは以下のようなフォーマットで格納されています。

```
[table],[start key],[timestamp].[encoded region name]
```

[table]はテーブル名で、[region start key]はそのリージョンの開始キー、そして[timestamp]はリージョンが作成された時間です。最後の[encoded region name]はそのリージョンの名前を表していて、[table]と[region start key]と[timestamp]のハッシュをとってエンコードされたものです。

### カラム

カラムは`info:regioninfo`と`info:seqnumDuringOpen`、`info:server`、`info:serverstartcode`などがあります。

info:regioninfoはエンコードされたリージョン名やその列キー、またはリージョンの開始キー/終了キーがなどの情報が含まれています。

info:seqnumDuringOpenはそのリージョンのデフォルトのレプリカIDのシーケンス番号が入っています。本筋とあまり関係がないので説明は省略します。

info:serverには、そのリージョンがアサインされているリージョンサーバの情報が入っています。

info:serverstartcodeには、そのリージョンがスタートした時間が入っています。

## 実データ

実データはHDFS上に格納されています。HBaseのデータディレクトリを見てみると以下のような構造になっています。

```
$ hdfs dfs -ls /apps/hbase/data
Found 9 items
drwxr-xr-x   - hbase hdfs          0 2017-01-08 12:14 /hbase/.hbase-snapshot
drwxr-xr-x   - hbase hdfs          0 2017-01-09 10:36 /hbase/.migration
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/.tmp
drwxr-xr-x   - hbase hdfs          0 2017-01-09 05:40 /apps/hbase/data/WALs
drwxr-xr-x   - hbase hdfs          0 2017-01-09 05:50 /apps/hbase/data/archive
drwxr-xr-x   - hbase hdfs          0 2016-10-25 07:49 /apps/hbase/data/data
-rw-r--r--   1 hbase hdfs         42 2016-10-25 07:48 /apps/hbase/data/hbase.id
-rw-r--r--   1 hbase hdfs          7 2016-10-25 07:48 /apps/hbase/data/hbase.version
drwxr-xr-x   - hbase hdfs          0 2017-01-09 06:50 /apps/hbase/data/oldWALs
```

`.hbase-snapshot`ディレクトリには、スナップショット関連のファイルが含まれています。`.migration`ディレクトリはHBaseのアップグレードを行う際に用いられます。`.tmp`ディレクトリは一時作業時に作られるファイルのためにあるディレクトリです。hbckもこのディレクトリを利用します。`WALs`ディレクトリはすべてのリージョンサーバのアクティブなWALファイルが格納されているディレクトリです。`archive`ディレクトリは.hbase-snapshotディレクトリも関連しますが、スナップショットのみで使われるディレクトリです。archiveディレクトリには、スナップショットのために必要なHFileが格納されてます。`hbase.id`ファイルはクラスタのユニークなIDが書き込まれているファイルです。`hbase.version`ファイルは名前の通りHBaseのバージョンが含まれているファイルです。`oldWALs`ディレクトリはHBaseレプリケーションに関連したディレクトリです。アクティブなWALはWALsディレクトリにありますが、アクディブでないかつまだレプリケーションされてないWALファイルはoldWALsに格納されます。

HBaseの実際のデータは`data`ディレクトに格納されています。ここからはこのディレクトリにフォーカスして説明します。

dataディレクトリは以下のような構造になっています。

```
$ hdfs dfs -ls /apps/hbase/data/data
Found 2 items
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/data/default
drwxr-xr-x   - hbase hdfs          0 2016-10-25 07:49 /apps/hbase/data/data/hbase
```

dataディレクトリ以下には、ネームスペースのディレクトリがあります。この環境ではデフォルトのネームスペースである`default`と、HBaseのシステムテーブルが所属するネームスペースの`hbase`があります。

それぞれのネームスペースのディレクトリをを見てみると以下のようになります。

```
$ hdfs dfs -ls /apps/hbase/data/data/default
Found 1 items
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/data/default/test
$ hdfs dfs -ls /apps/hbase/data/data/hbase
Found 4 items
drwxr-xr-x   - hbase hdfs          0 2016-10-25 07:49 /apps/hbase/data/data/hbase/acl
drwxr-xr-x   - hbase hdfs          0 2016-10-25 07:49 /apps/hbase/data/data/hbase/backup
drwxr-xr-x   - hbase hdfs          0 2016-10-25 07:48 /apps/hbase/data/data/hbase/meta
drwxr-xr-x   - hbase hdfs          0 2016-10-25 07:49 /apps/hbase/data/data/hbase/namespace
```

ネームスペースのディレクトリの下には、テーブルのディレクトリがあります。この環境ではdefaultネームスペース以下に`test`テーブルがあり、hbaseネームスペース以下には`acl`テーブル、`backup`テーブル、`meta`テーブル、そして`namespace`テーブルがあります。

さらにtestテーブルを見ていきます。

```
hdfs dfs -ls /apps/hbase/data/data/default/test
Found 4 items
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/data/default/test/.tabledesc
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/data/default/test/.tmp
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af
drwxr-xr-x   - hbase hdfs          0 2017-01-09 07:23 /apps/hbase/data/data/default/test/2e2531f7c70a6fc178ac3472193ab26b
```

`.tabledesc`ディレクトリは、`.tableinfo.000000xxxx`という名前のファイルが含まれていて、xxxxはシーケンス番号になっています。.tableinfoファイルには、hbaseのシェル上で`describe`した時に取得できる情報と同じものが含まれています。例えば、ブロックエンコーディングタイプやブルームフィルターを使うかどうか、保持するバージョン数や圧縮などです。そして`.tmp`ディレクトリは一時作業時に作られるファイルのためにあるディレクトリです。例えば.tableinfoファイルはまず.tmpディレクトリ以下で作成され、.tabledescディレククトリに移動されます。それ以外のディレクトリはエンコードされたリージョン名となっていて、リージョンのデータが格納されています。

リージョンのディレクトリは以下のようになっています。

```
hdfs dfs -ls -R /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af
-rw-r--r--   1 hbase hdfs         40 2017-01-09 07:23 /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af/.regioninfo
drwxr-xr-x   - hbase hdfs          0 2017-01-09 10:09 /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af/.tmp
drwxr-xr-x   - hbase hdfs          0 2017-01-09 10:09 /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af/cf
-rw-r--r--   1 hbase hdfs       4956 2017-01-09 10:09 /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af/cf/bda632095b304a74866a0d99979448aa
-rw-r--r--   1 hbase hdfs       4962 2017-01-09 10:09 /apps/hbase/data/data/default/test/0e984bc5624b8bd5d7f7e3c29fef38af/cf/ca33589a346943d88e09f4fe78416f5b
```

`.regioninfo`ファイルはリージョンの情報が含まれています。`.tmp`ディレクトリは一時作業時に作られるファイルのためにあるディレクトリでメジャーコンパクション等の処理で使われます。そしてカラムファミリ`cf`ディレクトリの下に実際のデータファイル(HFile)である`bda632095b304a74866a0d99979448aa`と`ca33589a346943d88e09f4fe78416f5b`ファイルがあります。

# hbckの使い方について

それでは、HBaseの内部的なデータの持ち方について説明したので、hbckの使い方について説明します。

hbckの基本的な使い方は以下になります。

```
$ hbase hbck
```

このコマンドを実行すると最後にOKかINCONSISTENTが表示されます。INCONSISTENTが表示された場合はデータに不整合があるということになります。

下記のように`-details`オプションをつけるとより詳細な説明が表示されます。

```
$ hbase hbck -details
```

以下のように特定のテーブルだけ指定して、チェック・修復を行うこともできます。

```
$ hbase hbck table1 table2
```

# 検出できる不整合とその直し方

検出できる不整合とその直し方をエラーメッセージ別に説明していきます。

## メタデータ、アサイン系のエラーメッセージ

まず、はじめにメタデータやアサイン系の不整合についてです。
この不整合を検出するためにhbckは以下の情報を使います。

* 各リージョンサーバに実際にデプロイされているリージョンの情報
* hbase:metaの情報
* .regioninfoファイルの情報

基本的な考え方としては、HDFSに置かれている「.regioninfoファイルの情報」を正として修正を行います。ここで説明するオプションではHDFSのデータの変更はしません。

以下のオプションを使って不整合の修復を行います。

|オプション|説明|
|---|---|
|-fix|リージョンのアサインメントの修復を行います。後方互換のために残されているオプションです。|
|-fixAssignments|リージョンのアサインメントの修復を行います。-fixを置き換えたものです。|
|-fixMeta|メタデータの修復を行います。|
|-fixEmptyMetaCells|hbase:metaのinfo:regioninfoカラムの内容が空のエントリを削除します。|
|-fixSplitParents|オフラインのsplit parentを強制的にオンラインにします。|

以下、エラーメッセージとその修復方法になります。

### "META region or some of its attributes are null."

hbase:metaのリージョンの情報がないか、その中の属性データが欠落している。

hbase:metaのリージョンの情報はZookeeper上に保存されていますがそれが無いか、情報が欠落している場合に起きるエラーです。この状態を修復するためには、クラスタを再起動するか、場合によっては特定のZookeeperのznodeをクリアする必要があります。

### "hbase:meta, replicaId A is not found on any region."

レプリカIDがAのhbase:metaのリージョンがどのサーバにもデプロイされていない。

レプリカIDの説明は以下です。
HBaseは基本的には1つのリージョンは1つのリージョンサーバに割り当てられますが、Region Replicasという機能を使うことで、1つのリージョンに対してプライマリとセカンダリのレプリカを複数のサーバに割り当てることができます。その際にそれらのレプリカにIDが割り振られそれをレプリカIDと呼びます。Region Replicasについての詳細は以下をご覧ください。

https://docs.hortonworks.com/HDPDocuments/HDP2/HDP-2.5.3/bk_hadoop-high-availability/content/ch_HA-HBase.html

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、デプロイされていないリージョンをサーバにデプロイします。

### "hbase:meta, replicaId A is found on more than one region."

レプリカIDがAのhbase:metaのリージョンが複数サーバにデプロイされている。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、重複してデプロイされている不必要なリージョンをクローズさせます。

### "hbase:meta replicas are deployed in excess. Configured A, deployed B"

hbase:metaのレプリカが設定より多くデプロイされている。A個で設定されているが、実際にはB個デプロイされている。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、不必要なリージョンのレプリカをクローズさせます。

### "Empty REGIONINFO_QUALIFIER found in hbase:meta"

hbase:metaの中でinfo:regioninfoカラムの内容が空。

このエラーは`-fixEmptyMetaCells`オプションを指定することで修復できます。このときhbckは、info:regioninfoカラムが空の列を削除します。

### "Region A, key=B, not on HDFS or in hbase:meta but deployed on X"

リージョンA(エンコードされたリージョン名B)は、HDFS(実データ)とhbase:metaにエントリがないのにサーバXにデプロイされている。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、そのリージョンをサーバからアンデプロイします。

### "Region A on HDFS, but not listed in hbase:meta or deployed on any region server"

リージョンAは実データがHDFS上にはあるが、hbase:metaにエントリがなく、どのサーバにもデプロイされていない。

このエラーは`-fixAssignments`オプションと`-fixMeta`オプションを指定することで修復できます。このときhbckは、HDFS上の.regioninfoからhbase:metaのエントリを復旧し、そのリージョンをいずれかのサーバにアサインします。

### "Region A not in META, but deployed on X"

リージョンAはhbase:metaにエントリがないのに、サーバXにデプロイされている(実データも存在する)。

このエラーは`-fixAssignments`オプションと`-fixMeta`オプションを指定することで修復できます。このときhbckは、HDFS上の.regioninfoからhbase:metaのエントリを復旧し、そのリージョンをいずれかのサーバに再アサインします。

### "Region A found in META, but not in HDFS or deployed on any region server."

リージョンAはMETA上にはあるが、実データがHDFSに存在せず、サーバにもデプロイされていない。

このエラーは`-fixMeta`オプションを指定することで修復できます。このときhbckは、META上からデータを削除します。

### "Region A found in META, but not in HDFS, and deployed on X"

リージョンAはhbase:meta上にはあるが、実データがHDFSに存在せず、サーバXにデプロイされている。

このエラーは`-fixAssignments`オプションと`-fixMeta`オプションを指定することで修復できます。このときhbckは、hbase:meta上からデータを削除し、そのリージョンをサーバからアサインを外します。

### "Region A not deployed on any region server."

リージョンAはどのサーバにもデプロイされていない。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、そのリージョンをいずれかのサーバに再アサインします。

### "Region A should not be deployed according to META, but is deployed on X"

リージョンAはhbase:metaによるとどこにもデプロイされてないはずだが、実際にはサーバXにデプロイされている。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、そのリージョンをクローズし強制的にオフラインにします。

### "Region A is listed in hbase:meta on region server X but is multiply assigned to region servers Y,Z"

リージョンAはhbase:metaに存在しサーバXにデプロイされてるはずだが、実際には複数のサーバYとZにデプロイされている。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、それらリージョンをクローズし強制的にオフラインにします。

### "Region A listed in hbase:meta on region server X but found on region server Y"

リージョンAはhbase:metaに存在しサーバXにデプロイされてるはずだが、実際にはサーバYにデプロイされている。

このエラーは`-fixAssignments`オプションを指定することで修復できます。このときhbckは、それらリージョンをクローズし強制的にオフラインにし、再アサインされるのを待ちます。

### "Region A is a split parent in META, in HDFS, and not deployed on any region server. This could be transient."

リージョンAはhbase:metaとHDFS上ではsplit parentになってるが、どのサーバにもデプロイされてない。移行中の可能性もある。

リージョンが分割されると、オフラインになった親リージョンは自動的にクリーンアップされますが、たまに残ってしまう場合があります。このときその情報はhbase:metaにはあってHDFS上にデータは残っていますがサーバにデプロイされていない状態になります。このときに`-fixSplitParents`オプションを指定してhbckを実行すると、分割された状態をリセットしてその親リージョンをオンラインにします。そしてさらにオーバラップ系のオプションを指定することで子リージョンとマージすることもできます。

# まとめ

今回は、HBaseのメタデータと実データについて実際にどう格納されているかについて説明し、メタデータ、アサイン系のエラーメッセージとその直し方について書きました。次回はそれ以外のエラーメッセージとその直し方について説明します。