说说HBase里的数据模型。我们可以使用RDBMS的术语来描述HBase,但并不准确;更好的做法是把HBase当做一个多维Map。下面会以hbase shell的操作为例来介绍HBase的数据模型,当然你也可以用java。 我用的HBase的版本是1.1.3。

跟一般的RDBMS不同,创建表时,需要指定表的列族,而不是具体的列。

create 'test', 'cf'

行是按字母顺序存储的。在分布式架构中,表会按row key拆分为多块以分布在不同的Resigon Server(实际存储数据的机器),所以为了后面业务处理尽量少发生不同rs间的shuffle,需要小心规划,让相关的列挨着,尽量分布在同一个rs上。

table splits

举例来说,如果row key是网站的名称,那么建议按倒序来设置,比如’org.apache.www’, ‘org.apache.mail’, ‘org.apache.jira’,而不是相反。

row key必须是可见字符。

HBase中的列表示为 “Column Family:Column Qualifier”。列族是HBase中一个特别重要的概念,我之前一直不太能接受,但如果从列式存储的角度来看,列族是必须具备的特性。 列族有点像ADS中的tablegroup表组,同一个列族的数据会存储在一起。不同的列族可以设置不同的策略,例如是否使用内存缓存,是否压缩,以及row key编码(IO策略是设置在列族这个level的,而不是列)。

Column Famliy

表有多个列族,每个列族中有多个列。表中每行数据都有所有的列族,但可能没有数据。

物理存储上,每个Region Server上有多个Region对象,对应表的Region;每个Region对象中有多个Store,每个store对应一个column family。如下图,store是最基础的物理存储单元,所以需要将有相近IO需求的列划分到同一个列族中。图中其他Memstore/Hfile/Hlog内容,下次再说。

物理存储

Column Qulifier

在列族中指定具体的列。列族比较固定,但列有很多类型。 HBase的元数据中不会存储列的数据,需要使用者自己搞定。

Cell

{行,列,版本}指定一个cell。cell可以为空。

NameSpace

可以把NameSpace理解为RDBMS的“数据库”。一个NameSpace代表了一组表,在多租户的场景下比较有用:

  • Quota管理
  • 安全管理
  • 指定Region Server Subset,以提供一定程度的孤岛
hbase(main):067:0* create_namespace 'myns1'
0 row(s) in 0.0150 seconds

hbase(main):068:0> create 'myns1:test1', 'cf1','cf2','cf3'
0 row(s) in 1.2220 seconds

=> Hbase::Table - myns1:test1

逻辑视图

下图是一个修改过的表。从逻辑视图来看,HBase中的数据是一个多维Map,Row Key + Time Stamp + ColumnFamily定位一个cell。cell中可以没有数据,所以HBase是松散的。

Row Key Time Stamp ColumnFamily(contents) ColumnFamily (anchor) ColumnFamily (people)  
“com.cnn.www” t9   anchor:cnnsi.com = “CNN”    
“com.cnn.www” t8   anchor:my.look.ca = “CNN.com”    
“com.cnn.www” t6 contents:html = “<html>…”      
“com.cnn.www” t5 contents:html = “<html>…”      
“com.cnn.www” t3 contents:html = “<html>…”      

物理视图

物理上,HBase按Column Family存储的,即同一列的数据是存储在同一个文件中(意会)。在创建表的时候,需要指定该表的列族;如果后面发现需要新增列族,必须alter显示增加。

hbase(main):020:0> create 'test2', 'cf1'
0 row(s) in 1.2450 seconds

=> Hbase::Table - test2
hbase(main):021:0> alter 'test2', NAME => 'cf24'
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.9870 seconds

但是列可以直接增加。

hbase(main):025:0> put 'test2','1', 'cf1:a', 'xiaoming'
0 row(s) in 0.0870 seconds

hbase(main):026:0> put 'test2','1', 'cf1:b', 'xiaoming'
0 row(s) in 0.0060 seconds

hbase(main):028:0> scan 'test2'
ROW                                              COLUMN+CELL
 1                                               column=cf1:a, timestamp=1458727083525, value=xiaoming
 1                                               column=cf1:b, timestamp=1458727091035, value=xiaoming
1 row(s) in 0.0100 seconds

数据操作

HBase的基础数据操作支持put/get/delete/scan。HBase本身不支持join,但是上层业务可以在HBase基础上做MR来支持。后面把hive跟hbase调试好以后看看join是怎么支持的。

hbase(main):071:0* put 'myns1:test1', '1', 'cf1:c1','red'
0 row(s) in 0.0140 seconds

hbase(main):072:0> put 'myns1:test1', '1', 'cf1:c2','blue'
0 row(s) in 0.0050 seconds

hbase(main):076:0> get 'myns1:test1', '1', 'cf1:c1'
COLUMN                                           CELL
 cf1:c1                                          timestamp=1458730792510, value=red
1 row(s) in 0.0110 seconds

hbase(main):077:0> put 'myns1:test1', '1', 'cf1:c1','not red'
0 row(s) in 0.0050 seconds

hbase(main):078:0> get 'myns1:test1', '1', 'cf1:c1'
COLUMN                                           CELL
 cf1:c1                                          timestamp=1458730842722, value=not red
1 row(s) in 0.0060 seconds

hbase(main):079:0> scan 'myns1:test1'
ROW                                              COLUMN+CELL
 1                                               column=cf1:c1, timestamp=1458730842722, value=not read
 1                                               column=cf1:c2, timestamp=1458730801549, value=blue
1 row(s) in 0.0120 seconds
hbase(main):080:0> delete 'myns1:test1', '1', 'cf1:c1'
0 row(s) in 0.0160 seconds

hbase(main):081:0> get 'myns1:test1', '1', 'cf1:c1'
COLUMN                                           CELL
0 row(s) in 0.0070 seconds

VERSIONS

跟一般的RDBMS不同,行+列不能唯一指定cell,必须加上versions。versions一般是timestamp,自增长。0.96之前的版本,默认保存3个版本;从0.96版本开始,默认version保存1个版本,如果用户需要多版本,需要自己指定。versions特性对于需要保留旧数据的应用来说会比较有用,但从官方默认值的变化来看,可能应用场景不多。 用户可以获取不同version存储的数据;数据按version倒序存储,用户获取时若不指定version则获取到的是最新的数据。 用户还可以修改旧version的数据。btw,这真是个奇怪的特性,用在什么情况下呢?

#指定多版本
hbase(main):099:0> alter 'myns1:test1', NAME=>'cf2', VERSIONS=>3
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.8940 seconds
hbase(main):004:0> put "myns1:test1", "1", "cf2:c1", "hehe"
0 row(s) in 0.0850 seconds
hbase(main):006:0> put "myns1:test1", "1", "cf2:c1", "haha"
0 row(s) in 0.0060 seconds
#默认获取最新
hbase(main):010:0> get "myns1:test1", "1", "cf2:c1"
COLUMN                                           CELL
 cf2:c1                                          timestamp=1458739686650, value=haha
1 row(s) in 0.0110 seconds
#所有
hbase(main):013:0> get "myns1:test1", "1", {COLUMN=>"cf2:c1", VERSIONS=>3}
COLUMN                                           CELL
 cf2:c1                                          timestamp=1458739686650, value=haha
 cf2:c1                                          timestamp=1458739676120, value=hehe
2 row(s) in 0.0120 seconds
#指定版本获取
hbase(main):015:0> get "myns1:test1", "1", {COLUMN=>"cf2:c1", TIMESTAMP=>1458739676120}
COLUMN                                           CELL
 cf2:c1                                          timestamp=1458739676120, value=hehe
1 row(s) in 0.0070 seconds
#修改旧version数据
hbase(main):017:0> put "myns1:test1", "1", "cf2:c1", "hoho", 1458739676120
0 row(s) in 0.0110 seconds
hbase(main):018:0> get "myns1:test1", "1", {COLUMN=>"cf2:c1", TIMESTAMP=>1458739676120}
COLUMN                                           CELL
 cf2:c1                                          timestamp=1458739676120, value=hoho
1 row(s) in 0.0200 seconds
#修改旧数据不影响获取最新
hbase(main):019:0> get "myns1:test1", "1", {COLUMN=>"cf2:c1"}
COLUMN                                           CELL
 cf2:c1                                          timestamp=1458739686650, value=haha
1 row(s) in 0.0100 seconds

ACID

HBase有限度的支持ACID。

HBase 官方链接