1. 概述(扯淡~)数据库
HBase是一帮家伙看了Google发布的一片名为“BigTable”的论文之后,犹如醍醐灌顶,进而“山寨”出来的一套系统。缓存
因而可知:分布式
1. 几乎全部的HBase中的理念,均可以从BigTable论文中获得解释。原文是英语的,并且还有很多数学概念,看了有点儿懵,建议网上找找学习笔记看看,差很少也就能够入门了。oop
2. Google确实牛X。性能
3. 老外也爱山寨~学习
第一次看HBase, 可能看到如下描述会懵:“基于列存储”,“稀疏MAP”,“RowKey”,“ColumnFamily”。google
其实没那么高深,咱们须要分两步来理解HBase, 就可以理解为何HBase可以“快速地”“分布式地”处理“大量数据”了。spa
1.内存结构翻译
2.文件存储结构设计
2. 名词概念以及内存结构
假设咱们有一张表(其中只有一条数据):
RowKey |
ColumnFamily : CF1 |
ColumnFamily : CF2 |
TimeStamp |
||
Column: C11 |
Column: C12 |
Column: C21 |
Column: C22 |
||
“com.google” |
“C11 good” |
“C12 good” |
“C12 bad” |
“C12 bad” |
T1 |
1) RowKey: 行键,可理解成MySQL中的主键列。
2) Column: 列,可理解成MySQL列。
3) ColumnFamily: 列族, HBase引入的概念:
4) TimeStamp:在每次跟新数据时,用以标识一行数据的不一样版本(事实上,TimeStamp是与列绑定的。)
那咱们为什么会获得HBase的读写高性能呢?其实全部数据库操做如何获得高性能,答案几乎都是一致的,就是作索引。
HBase的设计抛弃了传统RDBMS的行式数据模型,把索引和数据模型原生的集成在了一块儿。
以上图的表为例,表数据在HBase内部用Map实现,咱们把它写成JSon的Object表述,即:
{
"com.google": { CF1: { C11:{ T1: good } C12:{ T1: good } CF2: { C21:{ T1: bad } C22:{ T1: bad } } } }
因为Map自己能够经过B+树来实现,因此随机访问的速度大大加快(咱们须要想象一下,表中有不少行的状况)。
如今咱们在原来的表上修改一下(将Column: C22改成”good”):
RowKey |
ColumnFamily : CF1 |
ColumnFamily : CF2 |
TimeStamp |
||
Column: C11 |
Column: C12 |
Column: C21 |
Column: C22 |
||
“com.google” |
“C11 good” |
“C12 good” |
“C12 bad” |
“C12 bad” |
T1 |
“com.google” |
“C11 good” |
“C12 good” |
“C12 bad” |
“C12 good” |
T2 |
因而MAP变为了:
{
"com.google": { CF1: { C11:{ T1: good } C12:{ T1: good } CF2: { C21:{ T1: bad } C22:{ T1: bad T2:good } } } }
事实上,咱们只须要在C22的object再加一个属性便可。若是咱们把这个MAP翻译成表形状,也能够表示为:
RowKey |
ColumnFamily : CF1 |
ColumnFamily : CF2 |
TimeStamp |
||
Column: C11 |
Column: C12 |
Column: C21 |
Column: C22 |
||
“com.google” |
“C11 good” |
“C12 good” |
“C12 bad” |
“C12 bad” |
T1 |
|
|
|
|
“C12 good” |
T2 |
咱们发现,这个表里不少列是没有value的。想象一下,若是再加入一行RowKey不一样的数据,其中Column:C11内容为空,就能够在Json中省略该属性了。
好了,扯了这么多,就是为了说明HBase是“稀疏的高阶MAP”。
为了查询效率,HBase内部对RowKey作了排序,以保证相似的或者相同的RowKey都集中在一块儿,因而HBase就变成了一张“稀疏的,有序的,高阶的MAP”。有没有以为这样的表述很高冷? :)
3. 文件存储结构与进程模型
如上所述,HBase是一张“稀疏的,有序的,高阶的MAP”。
一般来讲,MAP能够用B+树来实现。B+树对查询性能而言表现良好,可是对插入数据有些力不从心,尤为对于插入的数据须要持久化到磁盘的状况而言。
咱们对RowKey作了排序,为了保证查询效率,咱们但愿将连续RowKey的数值保存在连续的磁道上,以免大量的磁盘随机寻道。因此在插入数据时,对于B+树而言,就面临着大量的文件搬移工做。
HBase使用了LSM树实现了MAP,简单说来,就是将插入/修改操做缓存在内存中,当内存中积累足够的数据后,再以块的形式刷入到磁盘上。
HBase的进程模型:
Region: 基于RowKey的分区,可理解成MySQL的水平切分。
每一个Region Server就是Hadoop集群中一台机器上的一个进程。
好比咱们的有1-300号的RowKey, 那么1-100号RowKey的行被分配到Region Server 1上,一样,101-200号分配到Region Server 2上, 201-300号分配到Region Server 3上。
在内存模型中,咱们说RowKey保证了相邻RowKey的记录被连续地写入了磁盘。在这里,咱们发现,RowKey决定了行操做(增,删,改,查)会被交与哪台Region Server操做。
让咱们假设一下,若是咱们的RowKey以记录的TimeStamp起始,从内存模型上说,这很合理,由于咱们可能面临大量的用户流水记录查询,查询的条件会设置一个时间片断,咱们但愿一次性从磁盘中读取这些流水记录,从而避免频繁的磁盘寻道操做。
可是再另外一方面,用户的流水记录查询会很频繁的出现“截至到至今”的查询条件,依照咱们上面的进程模型,Region Server 3必定会被分配到(由于最近的记录排在最后),这样就可能形成Region Server 3的“过热”,而Region Server 1“过冷”的状况。
文件存储模型:
在HDFS中,每张表对应一个目录,在表目录下,每一个Region对应一个目录,在Region目录下,每一个Store对应一个目录(一个Store对应一个ColumFamily)。结构以下:
HBase
|
---Table
|
---XXXX(Region的hash)
| |
| ----ColumnFamily
| |
| ---文件
|
---YYYYY(另外一个Region的hash)
咱们的新发现是,不一样的ColumnFamily对应不一样的Store, 而且被写入了不一样的目录, 这意味着:
1. 经过将一张表分解成了不一样的ColumnFamily,HBase能够从磁盘一次读取更少的内容(IO操做每每是计算机系统中最慢的一环)。
2. 咱们不该该将须要一次查询出的列,分解在不一样的ColumnFamily中,不然觉得着HBase不得不读取两个文件来知足查询要求。
另外,一个ColumnFamily中的每一列是连续存储的。即若是一个ColumnFamily中存在C1,C2两列,一段具备100行记录的存储格式是:
C1(1),C2(1),C1(2),C2(2),C1(3),C2(3).............C1(100),C2(100)
与其说HBase是基于列的数据库,更不如说HBase是基于“列族”的数据库。
4 理解:
基于以上的模型,大体的理解是:
1. RowKey决定了行操做任务进入RegionServer的数量,咱们应该尽可能的让一次操做调用更多的Region Server,已达到分布式的目的。
2. RowKey决定了查询读取连续磁盘块的数量,最理想的状况是一次查询,在每一个Region Server上,只读取一个磁盘块。
3. ColumnFamily决定了一次查询须要读取的文件数(不一样的文件不只意味着分散的磁盘块,还意味着屡次的文件打开关闭操做)。咱们应尽可能将但愿查询的结果集合并到一个ColumnFamily中。同时尽可能去除该ColumnFamily中不须要的列。
4. HBase官方建议尽可能的减小ColumnFamily的数量。
再瞎总结一下:
1. RowKey由查询条件决定。
2. ColumnFamily由查询结果决定。