

HBase是一种关系型的分布式数据库,可以用来存储海量的数据,满足海量数据的实时响应查询计算的应用要求。

在介绍正式内容之前,我们可以先想一想,大数据平台所提供的主要功能是什么?
大数据平台主要提供的是存储和计算,对于所学习的每一种组件,应该考虑其应用场景。
那么HBase除了能够应对海量数据的存储,同时也能够支撑实时响应查询计算。
接下来对HBase进行讲解。
HBase 基本介绍
HBase简介

HBase是谷歌的bigTable的开源实现,它能够提供高可靠、高性能、面向列、可伸缩的分布式存储服务。
采用谷歌的bigTable大表机制,存储的规模能够达到数十亿行级别以及数百万列的级别,并且对大数据的读和写能够达到实时响应的服务。
HBase的底层文件系统时采用的HDFS进行存储的,因此能够提供高可靠的文件读写。
HBase用zookeeper提供协同服务,zookeeper为HBase提供了三种服务,分别是:
- 分布式锁服务
- 监听服务
- 数据库服务
HBase与RDB的对比

上面是HBase与传统的关系型数据库的对比。
- 数据索引
对于HBase来说,它的数据索引仅仅只有行键作为的索引。
传统的关系型数据库,通常可以对不同的列,构建比较复杂的索引,来提高数据的访问性能。
但是HBase如果通过巧妙的设计,HBase的访问方法都能够利用行键对行键进行扫描,或者是扫描行键的某一个区域,从而查询到数据。使得整个系统的性能不至于下降。
- 数据维护
HBase的更新操作不会去替换当前的值,只会增加一个新的版本。
而对于传统的关系型数据库来说,新值将会替换旧值。旧值被覆盖后就不存在了。
- 可伸缩性
关系数据库很难实现横向扩展,纵向扩展的空间也比较有限。
相反,HBase和BigTable这些分布式数据库就是为了实现灵活的水平扩展而开发的,能够轻易地通过在集群中增加或者减少硬件数量来实现性能的伸缩。
HBase应用场景

- HBase是面向于海量数据存储的,存储的数据可以达到TB和PB级别。
- HBase不需要完全拥有关系型数据库的ACID特性
- A代表事务的原子性
- C代表事务的一致性
- I代表事务的独立性
- D代表事务的持久性
- HBase采用zookeeper为其提供服务,可以提供高吞吐量的客户端查询,在海量的数据中实现高效的随机读取机制,这是通过HBase行键实现的。
- HBase具有很强的性能伸缩能力,通过增加HBase的节点,能够水平扩展HBase的存储以及查询能力
- HBase能够同时处理结构化、半结构化、非结构化的数据
数据模型

- 总体上来说,应用程序是以表的方式在HBase存储数据的。
- 表是由行和列构成的,所有的列是从属于某一个列族的。
- 行和列的交叉点称之为cell,cell种存储的内容是具有版本化的。cell的内容是不可分割的字节数组。这个也是与传统的关系型数据库一个显著的区别。
- 表的行键也是一段字节数组,所以任何东西都可以保存进去,不论是字符串或者数字。
- 另外HBase的表是按key排序的,排序方式是针对字节的。
- 所有的表都必须要有主键-key。
Hbase表结构-1
可以看到Hbase每一行记录都是有行键的。
对于Hbase是有列族的概念,在列族下又有不同的列。
行键和列相交的地方成为cell,实际上是单元格的内容。
对于每一个单元格的内容有版本,版本是根据时间戳来决定的。
比如在t1时刻存进去的是male,那么在t2时刻对male进行修改,实际上存储的是female,那么male和female都存储在Hbase的cell种。
Hbase表结构-2

上面是对表结构的进一步解释
- 表:HBase采用表来组织数据,表由行和列组成,列划分为若干个列族。
- 行:每个HBase表都由若干行组成,每个行由行键(row key)来标识。
- 列族:一个HBase表被分组成许多“列族”(Column Family)的集合,列族里面有很多列,一般称这个列的名称为列限定符。列族是基本的访问控制单元。
- 列限定符:列族里的数据通过列限定符(或列)来定位。
- 单元格:在HBase表中,通过行、列族和列限定符确定一个“单元格”(cell),单元格中存储的数据没有数据类型,总被视为字节数组byte[]。
- 时间戳:每个单元格都保存着同一份数据的多个版本,这些版本采用时间戳进行索引。
数据存储概念视图

这是一个名为webtable的表,包含两个列族:contents和anchor
在这个例子里面
anchor有两个列 (anchor:aa.com, anchor:bb.com),
contents仅有一列(contents:html)
从表中可以发现,不同的行键、时间戳、列族、以及列限定符,构成一行数据。
并且可以从表中看到有一些地方是空白的。这表示HBase存储表示具有稀疏性。
数据存储物理视图

尽管在概念视图里,表可以被看成是一个稀疏的行的集合。但在物理上,它的是区分列族存储的。新的columns可以不经过声明直接加入一个列族。
对于新的列可以不加声明的直接加一个列族。
在程序运行的时候,可以通过添加数据的方式,不经过声明加入一个列族,这两个表是针对于不同列族的存储物理视图。
行存储

为进一步加深对HBase存储的理解,分别给出行存储和列存储的机制。
行存储,数据按行存储在底层文件系统中。通常,每一行会被分配固定的空间。
优点:有利于增加/修改整行记录等操作;有利于整行数据的读取操作。
缺点:单列查询时,会读取一些不必要的数据。
列存储

HBase采用列存储
列存储,数据以列为单位,存储在底层文件系统中。
大家可以看到这个图,这是一个传统的二维表结构,包含ID、Name、Phone、Address。那么针对于每一列数据,会把它取出来,当然,会做一些变化,为具体的每个单元格里面的值去加上一个键。
采用这种方式,它的优点是有利于面向单列数据的读取/统计等操作。比如想读取一批用户的电话号码,可以直接把相应的这一列取出来,就没有必要像行存储的样子,要去除所有的数据,然后再进行投影。
它的缺点:如果想要整行读取时,可能需要多次I/O操作。
HBase架构介绍
HBase架构介绍 - 1

HBase架构介绍
从上至下对HBase架构中的主要组件进行介绍
这里有client,client是HBase的客户端。client包含了一些接口。
HBase要使用到zookeeper集群,前面给大家介绍过zookeeper主要向HBase提供三种服务。
HBase架构中,HMaster是处于主控节点的地位,相应的slaver节点就是HRegionServer。
HMaster负责存储表的结构以及对HRegionServer进行控制,而RegionServer主要是用来存储数据以及返回客户端的查询请求。
在底层可以看到,HBase最终的文件是存储在HDFS上,这些文件最后都要转换成DataNode种的block。
HBase架构介绍 - 2

HBase的架构包括三个主要的功能组件:
- 库函数:链接到每个客户端
- 一个HMaster主服务器
-
HBase架构介绍 - 3

接下来对HBase的主要组件功能进行介绍 主服务器HMaster负责管理和维护HBase表的分区信息,维护HRegionServer列表,分配Region,负载均衡。
- HRegionServer负责存储和维护分配给自己的Region,处理来自客户端的读写请求。
请注意,如果有客户端要读或者写某些数据,客户端是直接向zookeeper发送请求的,zookeeper知道了要读和写的Region,然后找到相应的HRegionServer。从这个流程来看,客户端并没有直接跟Master进行通信。
- 客户端并不是直接从HMaster主服务器上读取数据,而是在获得Region的存储位置信息后,直接从HRegionServer上读取数据。
- 客户端并不依赖HMaster,而是通过Zookeeper来获得Region位置信息,大多数客户端甚至从来不和HMaster通信,这种设计方式使得HMaster负载很小 。也有特殊情况,比如说想创建表以及创建表的结构,这时候客户端是需要与Master进行通信的。
HBase架构介绍 - 4

接下来介绍HBase的存储架构
HBase中表是怎么一回事,表根据rowkey进行了横向划分,也就是起始rowkey和结束rowkey划成一个区域,用来存储Region。
一个Region下面包含若干个store,每一个store根据列族存储相应的Region数据。
store里面又分为memstore,memstore是一个缓存, 主要是方便临时写以及读取数据的查找。
再有就是storeFile,是每一个store把相应的Region数据存储到具体的物理表上。
再就是block,block是实际的一个具体的数据表。数据表采用block的方式,最后存储到HDFS上,这就是HBase的存储架构。
表和Region

来研究一下HBase中的表哥Region的关系。
开始的时候,HBase表开始只有一个Region,随着数据的不断增多,这个Redion就要不断的进行分裂。就是一个Region会分成两个Region,两个Region再分成多个Region。
比如上上图的中间这个表中又一个Region,就会分裂成右侧这个表中的两个Region。Region拆分操作非常快,接近瞬间,如果说此时有客户端请求去读取Redion中的数据,还是从被拆分的Region里面读取数据,并不受影响。知道分裂过程结束,把存储文件异步地写到独立的文件之后,才会从新的Region里面读相应的数据。
这就是表和Region的关系。
Region的定位 (1)

接下来学习Region的定位
在Hadoop的早期版本中采用的时三级模式。
就是采用zookeeper存储root表的位置。一般情况下root表只有一个Region,当然root表还是存储在HBase上。
root表中又记录了若干个meta表,meta表里面就存储了若干个region的位置信息。
因此客户端去查找某个具体Region里面数据的时候它经过三级模式。
在最新的HBase的版本中采用的是两级模式。
也就是采用zookeeper直接存储meta表的信息,而meta表里面又记录了用户表,也就是Region的具体信息。
这个过程是先找寻Meta Region地址。再由Meta Region找寻User Region地址。
新的这种方式较之前的方式少了一个级别。
Region的定位 - 2

- 为了加快访问速度,hbase:meta表会被保存在内存中。
- 假设hbase:meta表的每行(一个映射条目)在内存中大约占用1KB,并且每个Region限制为128MB。
- 两层结构可以保存的Region数目是128MB/1KB = 217个Region。
注意,即使采用的是两层架构,按照现在一般情况下设置每个Region的大小大概是1~2个GB。HBase也能够支撑海量数据的存储。
客户端

HMaster高可用

接下来介绍HMaster高可用性
采用zookeeper的锁机制,可以帮助集群的一个Master做集群的总管。其他的Master都作为备用Master,当一个Master当季的时候,通过投票机制可以选择备用的Master作为主Master,这样就避免了Master的单点失效问题。
HMaster

主服务器HMaster主要负责表和Region的管理工作:
- 管理用户对表的增加、删除、修改、查询等操作。
- 实现不同HRegionServer之间的负载均衡。
- 在Region分裂或合并后,负责重新调整Region的分布。
- 对发生故障失效的HRegionServer上的Region进行迁移。
HRegionServer

HRegionServer是HBase中最核心的模块。
- 负责维护分配给自己的Region。
- 响应用户的读写请求。
如果使用了zookeeper,HRegionServer还要报告自己的心跳信息给zookeeper。
HBase 关键流程
用户读写数据过程

- 用户写入数据时,首先是访问zookeeper,被分配到相应HRegionServer去执行。
- 用户数据首先被写入到Hlog中,得到一个返回值之后,再写入MemStore中,最终写到磁盘上形成StoreFile。
- 只有当操作写入Hlog之后,commit()调用才会将其返回给客户端。
- 当用户读取数据时, HRegionServer会首先访问MemStore缓存,如果找不到,再去磁盘上面的StoreFile中寻找。
缓存的刷新

- 系统会周期性地把MemStore缓存里的内容刷写到磁盘的StoreFile文件中,清空缓存,并在Hlog里面写入一个标记
- 每次刷写都生成一个新的StoreFile文件,因此,每个Store包含多个StoreFile文件
- 每个HRegionServer都有一个自己的HLog 文件,每次启动都检查该文件,确认最近一次执行缓存刷新操作之后是否有客户端在刷新的时候往Hlog写数据;如果发现更新,则先写入MemStore,再刷写到StoreFile,开始为用户提供服务
StoreFile的合并

- 每次刷写都生成一个新的StoreFile,数量太多,影响查找速度
- 调用Store.compact()把多个合并成一个
- 合并操作比较耗费资源,只有数量达到一个阈值才启动合并
Store工作原理

- Store是HRegionServer的核心
- 多个StoreFile合并成一个
- 单个StoreFile过大时,又触发分裂操作,1个父Region被分裂成两个子Region
HLog工作原理 - 1

- 分布式环境必须要考虑系统出错。HBase采用HLog保证系统恢复
- HBase系统为每个HRegionServer配置了一个HLog文件,它是一种预写式日志(Write Ahead Log)
- 用户更新数据必须首先写入日志后,才能写入MemStore缓存,并且,直到MemStore缓存内容对应的日志已经写入磁盘,该缓存内容才能被刷写到磁盘
HLog工作原理 - 2

- Zookeeper会实时监测每个HRegionServer的状态,当某个HRegionServer发生故障时,Zookeeper会通知HMaster.。
- HMaster首先会处理该故障HRegionServer上面遗留的HLog文件,这个遗留的HLog文件中包含了来自多个Region对象的日志记录。
- 系统会根据每条日志记录所属的Region对象对HLog数据进行拆分,分别放到相应Region对象的目录下,然后,再将失效的Region重新分配到可用的HRegionServer中,并把与该Region对象相关的HLog日志记录也发送给相应的HRegionServer 。
- HRegionServer领取到分配给自己的Region对象以及与之相关的HLog日志记录以后,会重新做一遍日志记录中的各种操作,把日志记录中的数据写入到MemStore缓存中,然后,刷新到磁盘的StoreFile文件中,完成数据恢复。共用日志优点:提高对表的写操作性能;缺点:恢复时需要分拆日志。
Hbase突出特点
多HFile的影响

如图所示,横坐标表示HFile文件的数目,纵坐标表示读取的时延。可以看到在HFile文件数目较少的情况下,读取的时延大概在两个毫秒左右。
但是随着HFile文件数目的不断增多,读取的时延将会越来越大。
如图所示,当HFile文件数目达到14400个左右的时候,读取的时延将达到20毫秒。上升了一个级别。
Compaction - 1

为了降低读取读取时延,采用了压缩的方法。
- Compaction(压缩)的目的,是为了减少同一个Region中同一个ColumnFamily下面的小文件(HFile)数目,从而提升读取的性能。
- Compaction分为Minor、Major两类:
- Minor:小范围的Compaction。有最少和最大文件数目限制。通常会选择一些连续时间范围的小文件进行合并。
- Major:涉及该Region该ColumnFamily下面的所有的HFile文件。
- Minor Compaction选取文件时,遵循一定的算法。
Compaction - 2

如图所示,这是minor压缩以及major压缩的区别。
可以看到minor只会对部分HFile进行压缩,采用major压缩的时候会对所有的HFile进行压缩。
要注意的是,不管是minor压缩还是major压缩,处于这两个阶段的时候都不能够读取数据,因此压缩式是会影响读写性能的。
OpenScanner

为了进一步提升读写的性能,采用了OpenScanner机制。
- OpenScanner的过程中,会创建两种不同的Scanner来读取Hfile、 MemStore的数据:
- HFile对应的Scanner为StoreFileScanner。
- MemStore对应的Scanner为MemStoreScanner。
BloomFilter

为了查找过程中缩小对数据的查找范围,采用BloomFilter
- BloomFilter用来优化一些随机读取的场景,即Get场景。 它可以被用来快速的判断一条用户数据在一个大的数据集合(该数据集合的大部分数据都没法被加载到内存中)中是否存在。
- BloomFilter在判断一个数据是否存在时,拥有一定的误判率。但对于“用户数据 XXXX不存在” 的判断结果是可信的。
- HBase的BloomFilter的相关数据,被保存在HFile中。
Hbase性能优化
行键(Row Key)

- 行键是按照字典序存储,因此,设计行键时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。
- 举个例子:如果最近写入HBase表中的数据是最可能被访问的,可以考虑将时间戳作为行键的一部分,由于是字典序排序,所以可以使用Long.MAX_VALUE - timestamp作为行键,这样能保证新写入的数据在读取时可以被快速命中。
构建HBase二级索引 - 1

- HBase只有一个针对行健的索引
- 访问HBase表中的行,只有三种方式:
- 通过单个行健访问
- 通过一个行健的区间来访问
- 全表扫描
HBase在新的版本中增加了coprocessor这个特性,基于这个特性有很多二级索引的工具
比如华为的Hindex,Hindex可以对多个表进行索引,也可以对多个列进行索引,也可以基于部分的列值进行索引。
当然,像redis还是有solor也都可以对HBase来进行二级索引。
构建HBase二级索引 - 2

- Hindex二级索引
- Hindex 是华为公司开发的纯 Java 编写的HBase二级索引,兼容 Apache HBase 0.94.8。当前的特性如下:
- 多个表索引
- 多个列索引
- 基于部分列值的索引
- Hindex 是华为公司开发的纯 Java 编写的HBase二级索引,兼容 Apache HBase 0.94.8。当前的特性如下:
HBase常用Shell命令
HBase常用Shell命令

- create:创建表
- list:列出HBase中所有的表信息
- put:向表、行、列指定的单元格添加数据
值得注意的是,我们向HBase的表中添加数据,我们只能够一个单元格一个单元格的添加数据,不能够整行添加数据。
- scan:浏览表的相关信息
- get:通过表名、行、列、时间戳、时间范围和版本号来获得相应单元格的值
- enable/disable:使表有效或无效
- drop:删除表
HBase中如果想要删除一个表,必须先使用disable停用表,然后再采取drop删除表
总结
思考题
HBase中的数据以什么形式存储?( )
- Int
- Long
- String
- Byte[]
HBase的分布式存储的最基本单元是?( )
- Region
- Column
- FamilyColumn
- Cell
