首先,还是拿出一张Hbase自带的结构图:
HBase采用了LSM-Tree来解决数据存储问题。其中有几个核心数据结构:
HRegion:一个独立的数据存储单元。存储从startkey-endkey的所有数据。HRegion中会根据每个columnfamily生成一个HStore对象
HStore:存储了hbase中每个columnfamily的数据记录。数据结构上主要包括一个Memstore和多个HStorefile
final SortedMap<Long, HStoreFile> storefiles = Collections.synchronizedSortedMap(new TreeMap<Long, HStoreFile>());
Memstore:LSM-Tree的内存树结构,在hbase 0.1.0版本中很简单,就是一个封装了的sortedMap,和一个snapshot
private final SortedMap<HStoreKey, byte[]> memcache = Collections.synchronizedSortedMap(new TreeMap<HStoreKey, byte []>());
volatile SortedMap<HStoreKey, byte[]> snapshot;
Storefile:0.1.0版本的HBase还使用的是Hadoop的Mapfile,Storefile类主要封装了Mapfile的主要方法。next,seek等
下面再来看下一条记录在HBase中是如何存储的。
我们都知道HBase采用了著名的列式数据库。贴一张淘宝大牛的列式存储结构图:
Row Key | Timestamp | Column Family | |
URI | Parser | ||
r1 | t3 | url=http://www.taobao.com | title=天天特价 |
t2 | host=taobao.com | ||
t1 | |||
r2 | t5 | url=http://www.alibaba.com | content=每天… |
t4 | host=alibaba.com |
Ø Row Key: 行键,Table的主键,Table中的记录按照Row Key排序
Ø Timestamp: 时间戳,每次数据操作对应的时间戳,可以看作是数据的version number
Ø Column Family:列簇,Table在水平方向有一个或者多个Column Family组成,一个Column Family中可以由任意多个Column组成,即Column Family支持动态扩展,无需预先定义Column的数量以及类型,所有Column均以二进制格式存储,用户需要自行进行类型转换。
在0.1.0版本中一条记录的key-value是这样的:<HStoreKey, byte []>
key:HStoreKey对象。主要存储了rowkey+column+timestamp。对hbase的增删查改都是基于HStoreKey的。
这就是为什么我们在scan或者get时如果指明column和timestamp会加快查找的原因。
还有一个可以发现的是,HStoreKey的column是cf+quality,因此,这就是为什么scan出来的result是每个cf+quality一行,而不是每个cf一行的原因。
value:byte[]。可以看到hbase对于value只是存储为byte数组。
这种<HStoreKey, byte []>存储方式正是典型的列式存储的实现方式。
虽然HStoreKey里column看起来有很多冗余。但这种自解释的数据结构扩展起来是非常方便的。
分布式系统中最不缺的就是存储空间,而且meta中column的长度有一定的限制,这样的冗余
是完全能接受的。