概述
在hbase中,数据是以表的形式存储的,表又是由列簇和行组成,几个基本概念如下:
1. 行(Row)
hbase的行(row)由一个row key和多组数据组成;
row是按照rowkey的字母排序来存储的;
2. 列簇(Column Family)
列簇在物理存储上包含了一系列Column的集合;
数据按ColumnFamily分开存储,HBase所谓的列式存储就是根据ColumnFamily分开存储(每一个ColumnFamily对应一个Store),每个列簇有自己的存储属性,包括数据是否压缩,是否在内存中缓存等。
3. 列修饰符(Column Qualifier)
每个列簇可以有一个或多个列修饰符,用来索引列簇中的数据,不需要在表定义时给出,新的列族成员可以随后按需、动态加入,不同row之间的列修饰符可以不同;
4. 列(Column)
hbase中的列是由列簇和列修饰符(Column Qualifier)组成,并以冒号隔开,表示一个具体的列;
5. 单元格(Cell)
Cell由行键,列族,列修饰符,时间戳唯一确定,时间戳表示版本;
Cell中的数据是没有类型的全部以字节码的形式贮存;
6. 时间戳(Timestamp)
时间戳在每次写入数据时候指定,默认是写入时RegionServer上的时间,但是也可以直接指定;
逻辑视图
以上,就是hbase数据模型的全部基本概念,下面可以看看逻辑视图如何。
Apache hbase是基于google开源的bigtabe,关于bigtable论文,可以参考这里。
类似于bigtable的原理,hbase也被设计成一个稀疏的,分布式的,可持久化的多维度排序map。
表结构和基本概念对应关系见下图:
其中第一行row00001对应的存储稀疏map如下:
物理视图
我们通过官网的一个描述示例来说明逻辑模型对应的物理存储模型。
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 = “…” | ||
“com.cnn.www” | t5 | contents:html = “…” |
其中有些单元格是空的,这个在hbase实际存储中是不占空间的,这也就是为什么hbase是“稀疏”的。
虽然hbase的表从逻辑上来说是一系列稀疏的map存储,但是从实际存储上来说是按照列簇维度存储的。比如上述官网的逻辑模型实际存储就是:
Row Key | Time Stamp | Column Family anchor |
---|---|---|
“com.cnn.www” | t9 | anchor:cnnsi.com = “CNN” |
“com.cnn.www” | t8 | anchor:my.look.ca = “CNN.com” |
和
Row Key | Time Stamp | ColumnFamily contents |
---|---|---|
“com.cnn.www” | t6 | contents:html = “…” |
“com.cnn.www” | t5 | contents:html = “…” |
“com.cnn.www” | t3 | contents:html = “…” |
数据模型的操作
hbase只提供四种针对数据模型的操作,即Put,Get,Delete和Scan,都是基于客户端api的Table类来进行操作,可以简单看下Scan的api用法,后面再详细讲。
public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Table table = ... // instantiate a Table instance
Scan scan = new Scan();
scan.addColumn(CF, ATTR);
scan.setRowPrefixFilter(Bytes.toBytes("row"));
ResultScanner rs = table.getScanner(scan);
try {
for (Result r = rs.next(); r != null; r = rs.next()) {
// process result...
}
} finally {
rs.close(); // always close the ResultScanner!
}
版本
hbase中有一个版本的概念,即version,我们通过{row, column, version}三元组可以定位一个Cell,可以理解为同一行同一列支持一组单元格存储,关于版本我们要知道:
1. 版本按照降序存储,即如果读一个版本,时间最近的数据将被读取;
2. 如果多次写入的版本相同,则只有最近一次写入的数据被保留;
3. 写入数据的版本不必是增序的;
4. hbase的最大版本个数可以在创建表时指定,也可以通过alter来修改,默认的是1(0.96版本及以后);
5. 在获取数据时,也可以指定版本个数;
拿Get操作来看,示例如下:
public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Get get = new Get(Bytes.toBytes("row1"));
get.setMaxVersions(3); // will return last 3 versions of row
Result r = table.get(get);
byte[] b = r.getValue(CF, ATTR); // returns current version of value
List<KeyValue> kv = r.getColumn(CF, ATTR); // returns all versions of this column