1 HBase 浅析
1.1 HBase 是啥
HBase 是一款面向列存储,用于存储处理海量数据的 NoSQL 数据库。它的理论原型是Google 的 BIgTable 论文。你可以认为 HBase 是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统。
HBase 的存储是基于HDFS的,HDFS 有着高容错性的特点,被设计用来部署在低廉的硬件上,基于 Hadoop 意味着 HBase 与生俱来的超强的扩展性和吞吐量。
HBase 采用的时key/value的存储方式,这意味着,即使随着数据量的增大,也几乎不会导致查询性能的下降。HBase 又是一个面向列存储的数据库,当表的字段很多时,可以把其中几个字段独立出来放在一部分机器上,而另外几个字段放到另一部分机器上,充分分散了负载的压力。如此复杂的存储结构和分布式的存储方式,带来的代价就是即便是存储很少的数据,也不会很快。
HBase 并不是足够快,只是数据量很大的时候慢的不明显。HBase主要用在以下两种情况:
单表数据量超过千万,而且并发量很大。
数据分析需求较弱,或者不需要那么实时灵活。
1.2 HBase 的由来
我们知道 MySQL 是一个关系型数据库,学数据库的时第一个接触的就是 MySQL 了。但是 MySQL 的性能瓶颈是很大的,一般单个table行数不宜超过500万行,大小不宜超过2G。
我们以互联网公司最核心用户表为例,当数据量达到千万甚至亿级别时候,尽管你可以通过各种优化来提速查询,但是对单条数据的检索耗时还是会超出你的预期!看下这个User表:

假如查询 id=1 这条数据对应的用户naMe,系统会给我们返回aa。但由于MySQL是以行为位单位存储的,当查 naMe 时却需要查询一整行的数据,连 age 和 eMAIl 也会被查出来!如果列非常多,那么查询效率可想而知了。
我们称列过多的表为宽表,优化方法一般就是对列进行竖直拆分:

此时查找 naMe 时只需要查找 User_baSiC 表,没有多余的字段,查询效率就会很快。如果一张表的行过多,会影响查询效率,我们将这样的表称之为高表,可以采用水平拆表的方式提高效率:

这种水平拆分应用比较多的 场景就是日志表,日志信息每天产生很多,可以按月/按日进行水平拆分,这样就实现了高表变矮。
上述的拆分方式貌似可以解决宽表跟高表问题,但是如果有一天公司业务变更,比如原来没有微信,现在需加入用户的微信字段。这时候需要改变表的结构信息,该怎么办?最简单的想法是多加一列,像这样:

但是你要知道不是所有用户都要微信号的,微信号这一列是设置默认值还是采取其他的做法就得权衡一下了。如果需扩展很多列出来,但不是所有的用户都有这些属性,那么拓展起来就更加复杂了。这时可以用下JSON格式的字符串,将若干可选择填写信息汇总,而且属性字段可以动态拓展,于是有了下边做法:

至此你可能认为这样存储数据它不挺好的嘛,用 HBase 出来干嘛?MySQL 有个致命缺点,就是当数据达到一定的阈值,无论怎么优化,它都无法达到高性能的发挥。而大数据领域的数据,动辄 PB 级数据量,这种存储应用明显是不能很好的满足需求的!并且针对上边的问题,HBase 都有很好的解决方案~~。
1.3 HBase 设计思路
接着上边说到的几个问题:高表、宽表、数据列动态扩展,把提到的几个解决办法:水平切分、垂直切分、列扩展方法 杂糅在一起。
有张表,你怕它又宽又高跟动态扩展列,那么在设计之初,就把这个表给拆开,为了列的动态拓展,直接存储JSON格式:

这样就解决了宽表跟列扩展问题,高表怎么办呢?一个表按行切分成paRtITion,各存一部分行:

解决了高表、宽表、动态扩展列 的问题后你会发现数据量大了速度不够快咋办?用缓存呗,查询出的数据放缓存中,下次直接从缓存拿数据。插入数据怎么办呢?也可以这样理解,我把要插入的数据放进缓存中,再也不用管了,直接由数据库从缓存拿数据插入到数据库。此时程序不需要等待数据插入成功,提高了并行工作的效率。
你用缓存的考虑服务器宕机后缓存中数据没来得及插入到数据库中造成丢数据咋办?参考 Redis 的持久化策略,可以插入数据这个操作添加一个操作日志,用于持久化插入操作,宕机重启后从日志恢复。这样设计架构就变成了这个样子:

这就是 HBase 实现的大致思路。接下来正式进入 HBase 设计解析。
2 Hbase 简介
Hbase 官网:http://hbase.Apache.oRg
2.1 HBase 特点
海量存储
HBase适合存储 PB 级别的海量数据,能在几十到百毫秒内返回数据。
列式存储
HBase是根据列族来存储数据的。列族下面可以有非常多的列,在创建表的时候列族就必须指定。
高并发
在并发的情况下,HBase的单个IO延迟下降并不多,能获得高并发、低延迟的服务。
稀疏性
HBase的列具有灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。
极易扩展
基于 RegionSeRveR 的扩展,通过横向添加 RegionSeveR 的机器,进行水平扩展,提升 HBase 上层的处理能力,提升HBase服务更多 Region 的能力。
基于存储的扩展(HDFS)。
2.2 HBase 逻辑结构
逻辑思维层面 HBase的存储模型如下:

Table(表):
表由一个或者多个列族构成。数据的属性如naMe、age、TTL(超时时间)等都在列族里边定义。定义完列族的表是个空表,只有添加了数据行以后,表才有数据。
ColuMn (列):
HBase 中的每个列都由 ColuMn FAMily(列族) 和 ColuMn QualifieR(列限定符)进行限定,例如 info:naMe、info:age。建表时只需指明列族,而列限定符无需预先定义。
ColuMn FAMily(列族):
多个列组合成一个列族。建表时不用创建列,在 HBase 中列是可增减变化的!唯一要确定的是列族,表有几个列族在开始创建时就定好的。表的很多属性,比如数据过期时间、数据块缓存以及是否使用压缩等都是定义在列族上的。
HBase 会把相同列族的几个列数据尽量放在同一台机器上。
Row(行):
一行包含多个列,这些列通过列族来分类。行中的数据所属的列族从该表所定义的列族中选取。由于HBase是一个面向列存储的数据库,所以一个行中的数据可以分布在不同的服务器上。
RowKey(行键):
RowKey 类似 MYsQL 中的主键,在 HBase 中 RowKey 必须有且 RowKey 是按照字典排序的,如果用户不指定 RowKey 系统会自动生成不重复字符串。查询数据时只能根据 RowKey 进行检索,所以 Table 的 RowKey 设计十分重要。
Region(区域):
Region 就是若干行数据的集合。HBase 中的 Region 会根据数据量的大小动态分裂,Region是基于HDFS实现的,关于Region的存取操作都是调用HDFS客户端完成的。同一个行键的 Region 不会被拆分到多个 Region 服务器上。
RegionSeRveR:
RegionSeRveR 就是存放Region的容器,直观上说就是服务器上的一个服务。负责管理维护 Region。
2.3 HBase 物理存储
以上只是一个基本的逻辑结构,底层的物理存储结构才是重中之重的内容,看下图

NaMespace:
命名空间,类似关系型数据库 DatabBase 概念,每个命名空间下有多个表。HBase有两个自带的命名空间,分别是hbase和deFAult,hbase 中存放的是 HBase 内置的表,deFAult 表是用户默认使用的命名空间。
TiMeStaMp:
时间戳,用于标识数据的不同版本(veRsion),每条数据写入时如果不指定时间戳,系统会自动添加为其写入 HBase 的时间。并且读取数据的时候一般只拿出数据的Type符合,时间戳最新的数据。之所以按照Type取数据是因为HBase的底层HDFS支持增删查,但不支持改。
Cell:
单元格,由 {Rowkey, coluMn FAMily:coluMn QualifieR, tiMe StaMp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存储。
3 HBase 底层架构

3.1 client
client 包含了访问 Hbase 的接口,另外 client 还维护了对应的 cache 来加速 Hbase 的访问,比如缓存元数据的信息。
3.2 ZookeepeR
HBase 通过 ZookeepeR 来做 Master 的高可用、RegionSeRveR 的监控、元数据的入口以及集群配置
