
上圖是HBase的存儲(chǔ)架構(gòu)圖。
由上圖可以知道,客戶端是通過Zookeeper找到HMaster,然后再與具體的Hregionserver進(jìn)行溝通讀寫數(shù)據(jù)的。
具體到物理實(shí)現(xiàn),細(xì)節(jié)包括以下這些:
1、客戶端在zookeeper中找到存儲(chǔ)了-ROOT-表的RegionServer
2、在-ROOT-中找到.META.表的位置
3、在.META.表中找到存儲(chǔ)了指定rowkey的HRegionServer位置
4、客戶端連接該機(jī)器進(jìn)行數(shù)據(jù)讀寫
首先要清楚HBase在hdfs中的存儲(chǔ)路徑,以及各個(gè)目錄的作用。在hbase-site.xml 文件中,配置項(xiàng) <name> hbase.rootdir</name> 默認(rèn) “/hbase”,就是hbase在hdfs中的存儲(chǔ)根路徑。以下是hbase0.96版本的個(gè)路徑作用。1.0以后的版本請(qǐng)參考這里: https://blog.bcmeng.com/post/hbase-hdfs.html

1、/hbase/.archive
HBase 在做 Split或者 compact 操作完成之后,會(huì)將 HFile 移到.archive 目錄中,然后將之前的 hfile 刪除掉,該目錄由 HMaster 上的一個(gè)定時(shí)任務(wù)定期去清理。
2、/hbase/.corrupt
存儲(chǔ)HBase損壞的日志文件,一般都是為空的。
3、/hbase/.hbck
HBase 運(yùn)維過程中偶爾會(huì)遇到元數(shù)據(jù)不一致的情況,這時(shí)候會(huì)用到提供的 hbck 工具去修復(fù),修復(fù)過程中會(huì)使用該目錄作為臨時(shí)過度緩沖。
4、/hbase/logs
HBase 是支持 WAL(Write Ahead Log) 的,HBase 會(huì)在第一次啟動(dòng)之初會(huì)給每一臺(tái) RegionServer 在.log 下創(chuàng)建一個(gè)目錄,若客戶端如果開啟WAL 模式,會(huì)先將數(shù)據(jù)寫入一份到.log 下,當(dāng) RegionServer crash 或者目錄達(dá)到一定大小,會(huì)開啟 replay 模式,類似 MySQL 的 binlog。
5、/hbase/oldlogs
當(dāng).logs 文件夾中的 HLog 沒用之后會(huì) move 到.oldlogs 中,HMaster 會(huì)定期去清理。
6、/hbase/.snapshot
hbase若開啟了 snapshot 功能之后,對(duì)某一個(gè)用戶表建立一個(gè) snapshot 之后,snapshot 都存儲(chǔ)在該目錄下,如對(duì)表test 做了一個(gè) 名為sp_test 的snapshot,就會(huì)在/hbase/.snapshot/目錄下創(chuàng)建一個(gè)sp_test 文件夾,snapshot 之后的所有寫入都是記錄在這個(gè) snapshot 之上。
7、/hbase/.tmp
當(dāng)對(duì)表做創(chuàng)建或者刪除操作的時(shí)候,會(huì)將表move 到該 tmp 目錄下,然后再去做處理操作。
8、/hbase/hbase.id
它是一個(gè)文件,存儲(chǔ)集群唯一的 cluster id 號(hào),是一個(gè) uuid。
9、/hbase/hbase.version
同樣也是一個(gè)文件,存儲(chǔ)集群的版本號(hào),貌似是加密的,看不到,只能通過web-ui 才能正確顯示出來
10、-ROOT-
該表是一張的HBase表,只是它存儲(chǔ)的是.META.表的信息。通過HFile文件的解析腳本 hbase org.apache.hadoop.hbase.io.hfile.HFile -e -p -f 可以查看其存儲(chǔ)的內(nèi)容,如下所示:
hbase org.apache.hadoop.hbase.io.hfile.HFile -e -p -f /hbase/-ROOT-/70236052/info/604443e666684d8ca2b3f81d662df6c3
解析內(nèi)容如下:
K: .META.,,1/info:server/1504514627434/Put/vlen=14/ts=0 V: dchbase2:60020
K: .META.,,1/info:serverstartcode/1504514627434/Put/vlen=8/ts=0 V: \x00\x00\x01]\xA0\xAA\x04A
以上可以看出,-ROOT-表記錄的.META.表的所在機(jī)器是dchbase2,與web界面看到的一致:

11、.META.
通過以上表能找到.META.表的信息,該表也是一張hbase表,通過以上命令,解析其中一個(gè)region:
hbase org.apache.hadoop.hbase.io.hfile.HFile -e -p -f /hbase/.META./1028785192/info/2de4d44c7a2e4d86928f3a075ad8471b
解析內(nèi)容如下:
K: adt_app_channel,,1439437512159.cd311ca186e08ee5aeaa4cc188121ba3./info:server/1498474805942/Put/vlen=14/ts=0 V: dchbase3:60020
K: dc_debug_log,EAA47BA32FD980D20E2DB2D19050A51D|2|638069812,1509447378983.3642c1134e2dcd810f48a0b0409f60b3./info:server/1509531353681/Put/vlen=14/ts=0 V: dckafka1:60020
K: dc_debug_log,EAA47BA32FD980D20E2DB2D19050A51D|2|638069812,1509447378983.3642c1134e2dcd810f48a0b0409f60b3./info:server/1509447380418/Put/vlen=14/ts=0 V: dckafka2:60020
以上可以看出,adt_app_channel表的數(shù)據(jù)記錄在dchbase3這臺(tái)reginserver上,也與界面一致,如果有多個(gè)region,則會(huì)在表名后面加上rowkey的范圍:

通過以上描述,只要找到-ROOT-表的信息,就能根據(jù)rowkey找到對(duì)應(yīng)的數(shù)據(jù),那-ROOT-在哪里找呢?從本文一開始的圖中可以知道,就是在zookeeper中找的。進(jìn)入zookeeper命令行界面:
./hbase zkcli -server dcnamenode1:2181(hbase自帶zk)
[zk: dcnamenode1:2181(CONNECTED) 3] get /hbase/root-region-server
12439@dchbase3dchbase3,60020,1498474547671 ###注意該行信息
cZxid = 0x2f0001c2c0
ctime = Mon Jun 26 19:00:04 CST 2017
mZxid = 0x2f0001c2c0
mtime = Mon Jun 26 19:00:04 CST 2017
pZxid = 0x2f0001c2c0
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 47
numChildren = 0
[zk: dcnamenode1:2181(CONNECTED) 4]
可以看出-ROOT-表存儲(chǔ)在 dchbase3 機(jī)器中,對(duì)應(yīng)界面如下:

以上就是HBase客戶端根據(jù)指定的rowkey從zookeeper開始找到對(duì)應(yīng)的數(shù)據(jù)的過程。
那在Region下HBase是如何存儲(chǔ)數(shù)據(jù)的呢?
以下就具體操作一張表,查詢對(duì)應(yīng)的HFile文件,看HBase的數(shù)據(jù)存儲(chǔ)過程。
在HBase創(chuàng)建一張表 test7,并插入一些數(shù)據(jù),如下命令:
create 'test7','info' #創(chuàng)建表
put 'test7','100',info:name','yann'
put 'test7','100',info:age','12'
put 'test7','200',info:name','yann'
put 'test7','200',info:age','15'
查看wal日志,通過 hbase org.apache.hadoop.hbase.regionserver.wal.HLog --dump -p 命令可以解析HLog文件,內(nèi)容如下:
hbase org.apache.hadoop.hbase.regionserver.wal.HLog --dump -p /hbase/.logs/server1,60020,1511338186832/server1%2C60020%2C1511338186832.1511593793118
Sequence 68663206 from region 1028785192 in table .META.
Action:
row: test7,,1511593978551.33ba34be458d2b18f97d7b7ccc89468e.
column: info:regioninfo
at time: Sat Nov 25 15:12:58 CST 2017
value: \x01\x00\x00\x00\x00\x01_\xF2\x05\xAA\xB76test7,,1511593978551.33ba34be458d2b18f97d7b7ccc89468e.\x00\x00\x05test7$\xB2\xBEV
Sequence 68663207 from region 1028785192 in table .META.
Action:
row: test7,,1511593978551.33ba34be458d2b18f97d7b7ccc89468e.
column: info:server
at time: Sat Nov 25 15:12:58 CST 2017
value: server1:60020
Action:
row: test7,,1511593978551.33ba34be458d2b18f97d7b7ccc89468e.
column: info:serverstartcode
at time: Sat Nov 25 15:12:58 CST 2017
value: \x00\x00\x01_\xE2\xC6\x98P
Sequence 68663208 from region 33ba34be458d2b18f97d7b7ccc89468e in table test7
Action:
row: 100
column: info:name
at time: Sat Nov 25 15:13:33 CST 2017
value: yann
Sequence 68663209 from region 33ba34be458d2b18f97d7b7ccc89468e in table test7
Action:
row: 100
column: info:age
at time: Sat Nov 25 15:13:41 CST 2017
value: 12
Sequence 68663210 from region 33ba34be458d2b18f97d7b7ccc89468e in table test7
Action:
row: 200
column: info:name
at time: Sat Nov 25 15:13:47 CST 2017
value: yann
Sequence 68663211 from region 33ba34be458d2b18f97d7b7ccc89468e in table test7
Action:
row: 200
column: info:age
at time: Sat Nov 25 15:13:51 CST 2017
value: 12
Sequence 68663212 from region 33ba34be458d2b18f97d7b7ccc89468e in table test7
Action:
row: METAROW
column: METAFAMILY:
at time: Sat Nov 25 15:14:19 CST 2017
value: HBASE::CACHEFLUSH
Sequence 68663213 from region 33ba34be458d2b18f97d7b7ccc89468e in table test7
Action:
row: 200
column: info:age
at time: Sat Nov 25 15:15:19 CST 2017
value: 15
查看HFile文件,內(nèi)容如下:
hbase org.apache.hadoop.hbase.io.hfile.HFile -e -p -f /hbase_1/test7/33ba34be458d2b18f97d7b7ccc89468e/info/caeaaf25659242c19e5f4f983c5206ee
K: 100/info:age/1511594021359/Put/vlen=2/ts=0 V: 12
K: 100/info:name/1511594013569/Put/vlen=4/ts=0 V: yann
K: 200/info:age/1511594031440/Put/vlen=2/ts=0 V: 12
K: 200/info:name/1511594027562/Put/vlen=4/ts=0 V: yann
由此可見,HFile文件就是存儲(chǔ)HBase的KV對(duì),其中Key的各個(gè)字段包含了的信息如下:
100/info:age/1511594021359/Put/vlen=2/ts=0
rowkey / columnFamily:column / timestamp / Option / len / ts
由于hbase把cf和column都存儲(chǔ)在HFile中,所以在設(shè)計(jì)的時(shí)候,這兩個(gè)字段應(yīng)該盡量短,以減少存儲(chǔ)空間。
但刪除一條記錄的時(shí)候,HBase會(huì)怎么操作呢?執(zhí)行以下命令:
deleteall 'test7','200'
刪除了rowkey為200的記錄,查看hdfs,原來的HFile并沒有改變,而是生成了一個(gè)新的HFile,內(nèi)容如下:
K: 100/info:/1511594203116/DeleteFamily/vlen=0/ts=0 V:
所以在HBase中,刪除一條記錄并不是修改HFile里面的內(nèi)容,而是寫新的文件,待HBase做合并的時(shí)候,把這些文件合并成一個(gè)HFile,用時(shí)間比較新的文件覆蓋舊的文件。HBase這樣做的根本原因是,HDFS不支持修改文件。