Hbase整体架构剖析

概念 Hbase是一个开源的、面向列的、稀疏的、适合存储海量非结构化和半结构化的、具备高性能、高可靠性、可灵活扩展的、支持实时数据读写的分布式数据储存系统。
典型特征:

  • 大表:一个表可以有上亿行,上百万个列;
  • 面向列:面向列(族)的存储、检索和权限控制;
  • 稀疏:表中为空(null)的列不占用存储空间。
  • 数据类型单一:HBase 中的数据类型只有一种String
  • 无模式:每行数据对应的列可以不相同,创建表时不需要指定列,插入数据时列可以任意添加
  • 数据多版本:列中的数据可以有多个版本,查询时可以通过指定版本号获取(版本号不同,获取到的数据不同)
Hadoop EcoSystem生态系统 Hbase整体架构剖析
文章图片

图描述了Hadoop EcoSystem中的各层系统,其中HBase位于结构化存储层,Hadoop HDFS为HBase提供了高可靠性的底层存储支持,Hadoop MapReduce为HBase提供了高性能的计算能力,Zookeeper为HBase提供了稳定服务和failover机制。
此外,Pig和Hive还为HBase提供了高层语言支持,使得在HBase上进行数据统计处理变的非常简单。 Sqoop则为HBase提供了方便的RDBMS数据导入功能,使得传统数据库数据向HBase中迁移变的非常方便。
HBase访问接口
  1. Native Java API,最常规和高效的访问方式,适合Hadoop MapReduce Job并行批处理HBase表数据
  2. HBase Shell,HBase的命令行工具,最简单的接口,适合HBase管理使用
  3. Thrift Gateway,利用Thrift序列化技术,支持C++,PHP,Python等多种语言,适合其他异构系统在线访问HBase表数据
  4. REST Gateway,支持REST 风格的Http API访问HBase, 解除了语言限制
  5. Pig,可以使用Pig Latin流式编程语言来操作HBase中的数据,和Hive类似,本质最终也是编译成MapReduce Job来处理HBase表数据,适合做数据统计
  6. Hive,当前Hive的Release版本尚没有加入对HBase的支持,但在下一个版本Hive 0.7.0中将会支持HBase,可以使用类似SQL语言来访问HBase

Hbase集群典型部署 Hbase整体架构剖析
文章图片

  • Master:又叫HMaster,负责Hbase中的Table和Region的管理,包括:表的增删改查、Region Server的负责均衡、Region的分布调整、Region的分裂以及分裂后Region的分配、Region Server失效后Region的迁移;
  • zookeeper cluster:存储root表的地址和Master地址,Region Server 主动向zookeeper注册,使得master可以随时感知Region Server的健康状态,避免Master单点故障;
  • Region Server:负责数据路由、数据读写和数据持久化,是Hbase的数据处理和计算单元,同时还负责区域分割。Region Server要求和HDFS的datanode一起部署。
  • HDFS:代表HDFS集群,Hbase的数据最终存储在DataNode的块(block)上。
Hbase的系统架构 Hbase整体架构剖析
文章图片

各个组件的作用如下:
  • Client
Client 数量为一个或多个,HBase Client 使用 HBase 的 RPC 机制与HMaster和HRegionServer进行通信。
  1. 对于管理类操作Client与HMaster进行RPC通信
  2. 对于数据读写操作Client与HRegionServer进行RPC通信
  • Zookeeper
  1. 保证集群中只有一个正在运行的HMaster,如果HMaster挂了,通过Zookeeper的选举机制保证集群中总有一个HMaster运行,避免单点问题
  2. 通过将集群各节点状态信息注册到Zookeeper中,使得HMaster可随时感知各个HRegionServer的健康状态
  • HMaster
  1. 为HRegionServer分配Region,调整Region的分布,管理HRegionServer的负载均衡
  2. HMaster中记录了Region在哪台Hregion server上,它管理所有的HRegionServer并告诉HRegionServer维护那些Region
  3. 在HRegion Server宕机后,将失效HRegion Server 上的Regions迁移到其它的HRegionServer
  • HRegionServer
  1. 维护Region,处理并响应这些Region的IO请求及响应
  2. HRegionServer管理了很多Table的分区,即Region,负责对超过阀值的Region进行切分(split)
HRegionServer主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。
Hbase整体架构剖析
文章图片

HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个 Region,HRegion中由多个HStore组成。每个HStore对应了Table中的一个Column Family的存储,可以看出每个Column Family其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个Column Family中,这样最高效。
HStore存储是HBase存储的核心了,其中由两部分组成,一部分是MemStore,一部分是StoreFiles。 MemStore是Sorted Memory Buffer,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile), 当StoreFile文件数量增长到一定阈值,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进 行版本合并和数据删除,因此可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的写操作只要 进入内存中就可以立即返回,保证了HBase I/O的高性能。当StoreFiles Compact后,会逐步形成越来越大的StoreFile,当单个StoreFile大小超过一定阈值后,会触发Split操作,同时把当前 Region Split成2个Region,父Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer 上,使得原先1个Region的压力得以分流到2个Region上。下图描述了Compaction和Split的过程:
Hbase整体架构剖析
文章图片

在理解了上述HStore的基本原理后,还必须了解一下HLog的功能,因为上述的HStore在系统正常工作的前提下是没有问 题的,但是在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegionServer意外退出,MemStore中的内存数据将会丢失,这就需 要引入HLog了。每个HRegionServer中都有一个HLog对象,HLog是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中(HLog文件格式见后续),HLog文件定期会滚动出新的,并 删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知 到,HMaster首先会处理遗留的 HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取 到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。
  • HRegion:当表的大小超过预设值的时候,HRegion会自动按行对rowkey对应的数据进行拆分
  • HLog:HLog 是一个实现了WAL(Write Ahead Log)的预写日志类,其内部是一个简单的顺序日志。每个HRegionServer上都有一个HLog,类似于mysql中的binlog。该日志只会追加内容,用于记录操作日志,能用来做灾难恢复(数据回滚到初始状态)。
  • HStore:它是HBase的存储核心,存储的最小单元,由MemStore或0至多个StoreFile组成。
  • MemStore:它是内存缓冲区,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile,是HBase存储数据的文件组织形式),当StoreFile的文件数量增长到一定阈值后,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进行版本合并和数据删除操作。

因此,可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的Compact过程中进行的,这样使得用户的写操作只要进入内存就可以立即返回,保证了HBaseI/O的高性能。
数据flush过程如下:
  1. 当memstore数据达到阈值(默认是64M),将数据刷到硬盘,将内存中的数据删除,同时删除Hlog中的历史数据。
  2. 并将数据存储到hdfs中。
  3. 在hlog中做标记点。
数据Compact合并过程如下:
  1. 当数据块达到4块,hmaster将数据块加载到本地,进行合并
  2. 当合并的数据超过256M,进行拆分,将拆分后的region分配给不同的hregionserver管理
  3. 当hregionser宕机后,将hregionserver上的hlog拆分,然后分配给不同的hregionserver加载,修改.META.(HBase 的内置表,存储HBase 内部的系统信息——Region的分布情况以及每个Region的详细信息)
  4. 注意:hlog会同步到hdfs
  • HBase 容错
  1. HMaster 容错:通过Zookeeper实现,如果HMaster挂掉,通过Zookeeper的选举机制重新选择一个新的HMaster
  2. HRegionServer容错:RegionServer会定时向Zookeeper发送心跳,如果一段时间内未出现心跳,Master将该RegionServer上的Region重新分配到其他RegionServer上。
  3. Zookeeper容错:zookeeper是为其它分布式框架提供分布式协调服务的,本身并不是单点,一般都是奇数个zk组成的集群,如果某一个挂掉后,zk会通过选举机制重新选择一个leader。
HBase存储格式 HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,主要包括上述提出的两种文件类型:
  1. HFile, HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile
  2. HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File
HFile
下图是HFile的存储格式:
Hbase整体架构剖析
文章图片

首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。正如图中所示的,Trailer 中有指针指向其他数据块的起始点。File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。Data Index和Meta Index块记录了每个Data块和Meta块的起始点。
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制。每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询。 每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏。后面会详细介绍每个KeyValue对的内部构造。
HFile里面的每个KeyValue对就是一个简单的byte数组。但是这个byte数组里面包含了很多项,并且有固定的结构。我们来看看里面的具体结构:
Hbase整体架构剖析
文章图片

开始是两个固定长度的数值,分别表示Key的长度和Value的长度。紧接着是Key,开始是固定长度的数值,表示RowKey 的长度,紧接着是RowKey,然后是固定长度的数值,表示Family的长度,然后是Family,接着是Qualifier,然后是两个固定长度的数 值,表示Time Stamp和Key Type(Put/Delete)。Value部分没有这么复杂的结构,就是纯粹的二进制数据了。
HLogFile
Hbase整体架构剖析
文章图片

上图中示意了HLog文件的结构,其实HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。

Hbase的数据模型 存储在HBase表每一行数据都有可排序的关键字( Row Key)和任意列项( Column &Column Family)。在HBase中,仅能通过主键( Row Key)和主键版本号来检索数据,仅支持单行事务。下面以HBase存储搜索引擎的网页为例:
Hbase整体架构剖析
文章图片

Hbase整体架构剖析
文章图片

Hbase核心术语介绍:
  • Row Key(行健)
与nosql数据库们一样,row key是用来检索记录的主键。访问HBASE table中的行,只有三种方式:
  1. 通过单个row key访问
  2. 通过row key的range(正则)
  3. 全表扫描
row key行键 (Row key)可以是任意字符串(最大长度 是 64KB,实际应用中长度一般为 10-100bytes),在HBASE内部,row key保存为字节数组。存储时,数据按照row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)
  • Columns Family(列族)
列簇 :HBASE表中的每个列,都归属于某个列族(表中的每个列都保存到某一个文件中,即一个列族对应一个文件)。列族是表的schema的一部 分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history,courses:math都属于courses 这个列族。
  • Cell(列)
由{row key, columnFamily, version} 唯一确定的单元。cell中 的数据是没有类型的,全部是字节码形式存储。
关键字:无类型、字节码
  • Time Stamp(时间戳)
hbase 中 每保存一条数据,它会默认为生成一个时间戳,也可以自己设置。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面
Hbase的物理模型 对于HBase 的物理模型我们分别从Table的分割、Region的拆分、Region的分布,Region的构成四个部分介绍,下图为HBase的物理模型图:
Hbase整体架构剖析
文章图片

  • Table的分割
Table 中的所有行都按照 rowkey 的字典序排列,Table 在行的方向上分割为多个Region,一个Region在同一时刻只能被同一个RegionServer管理,RegionServer可以管理多个Region(一对多)
Hbase整体架构剖析
文章图片


Region的拆分
Region是按大小分割的,新创建的表只有一个region(数据为空),随着数据增多,region不断增大,当增大到一个阀值(默认128M)时,region就会拆分为两个新的region,之后会有越来越多的region。
那么region为什么要进行拆分呐?随着数据越来越多,region越来越多,那么对region的管理就比较麻烦,在我们的大数据环境下,经常会面临大量的数据处理,拆分region是为了并行处理,提高效率。
Hbase整体架构剖析
文章图片

Region 的分布
Region 是 HBase 中分布式存储和负载均衡的最小单元,不同的Region分布到不同的RegionServer 上,如下图Table1 、Table2 中均有多个Region,这些Region 分布在不同的RegionServer 中
Hbase整体架构剖析
文章图片

Region的构成
Region虽然是分布式分布式存储的最小单元,但并不是存储的最小单元,Store是存储的最小单元
Region由一个或者多个Store组成,每个Store会保存一个Column Family;每个Store又由一个MemStore 或0至多个StoreFile 组成;MemStore 存储在内存中,StoreFile存储在HDFS中,下图为Region 的构成,当我们向HBase 插入数据时,会先存放到 memStore(内存)中,然后再从内存中存放到磁盘文件StoreFile 中,磁盘文件满了之后再存放到HDFS 中
Hbase整体架构剖析
文章图片

Hbase的工作原理
  • 写数据流程:
  1. client向hregionserver发送写请求。
  2. Regionserver找到目标Region。
  3. Region会检查数据是否与Schema一致。
  4. 若客户端没有指定时间戳,默认取当前时间。
  5. hregionserver将数据写到hlog(write ahead log)。为了数据的持久化和恢复。
  6. hregionserver将数据写到内存memstore(判断Memstore是否需要Flush为Storefile文件)
  7. 响应客户端Client请求
  • 读数据流程:
  1. Client访问ZK,查找-ROOT-表,获取.META.表信息
  2. 从.META.表查找,获取存放目标数据的HRegion信息,从而找到对应的HRegionServer
  3. 通过HRegionServer获取需要查找的数据
  4. Region会先从Memstore中查询,命中则返回(先查Memstore的好处是在内存中,查询快)。
  5. 若Memstore没有,则扫描StoreFile(这个过程可能会扫描很多StoreFIle),数据从内存和硬盘合并后缓存到内存中的数据块中最后响应给客户端
读取数据时zookeeper如何定位到Region,返回给Client数据?
HBase中有两张特殊的Table,-ROOT-和.META.
  1. .META.:记录了用户表的Region信息,.META.可以有多个regoin
  2. -ROOT-:记录了.META.表的Region信息,-ROOT-只有一个region
  3. Zookeeper中记录了-ROOT-表的location
-ROOT-和.META.是HBase内置的两张表,存储了HBase内部的一些系统信息,.META.存储Region的元数据(Region的分布情况以及每个Region的详细信息),随着HRegion的增多,.META.表中的数据也会增大,并拆分成多个新的HRegion。为了定位.META.表中各个HRegion的位置,把.META.表中所有HRegion的元数据保存在-ROOT-表中。
所有客户端访问用户数据前,需要首先访问Zookeeper获得-ROOT-的位置,然后访问-ROOT-表获得.META.表的位置,最后根据.META.表中的信息确定用户数据存放的位置,查找到用户数据,最后放回给Client,流程图如下
Hbase整体架构剖析
文章图片

七、Hbase 检索时间复杂度
既然使用 Hbase 的目的是高效、高可靠、高并发的访问海量非结构化数据,那么 Hbase 检索数据的时间复杂度是关系到基于 Hbase 的业务系统开发设计的重中之重,Hbase 的运算有多快,本文从计算机算法的数学角度做简要分析,以便读者理解后文的项目实例中 Hbase 业务建模及设计模式中的考量因素。
我们先以如下变量定义 Hbase 的相关数据信息:
  • n=表中 KeyValue 条目数量(包括 Put 结果和 Delete 留下的标记)
  • b=HFile 李数据库(HFile Block)的数量
  • e=平均一个 HFile 里面 KeyValue 条目的数量(如果知道行的大小,可以计算得到)
  • c=每行里列的平均数量
如上图我们可以看出,Hbase 检索一条客户数据需要的处理过程大致如下:
  1. 如果不知道行健,直接查找列 key-value 值,则你需要查找整个 region 区间,或者整个 Table,那样的话时间复杂度是 O(n),这种情况是最耗时的操作,通常客户端程序是不能接受的,我们主要分析针对行健扫描检索的时间复杂度情况,也就是以下 2 至 4 步骤的内容。
  2. 客户端寻找正确的 RegionServer 和 Region。话费 3 次固定运算找到正确的 region,包括查找 ZooKeeper,查找-ROOT-表,找找.META 表,这是一次 O(1) 运算。
  3. 在指定 Region 上,行在读过程中可能存在两个地方,如果还没有刷写到硬盘,那就是在 MemStore 中,如果已经刷写到硬盘,则在一个 HFile 中。假定只有一个 HFile,这一行数据要么在这个 HFile 中,要么在 Memstore 中。
  4. 对于后者,时间复杂度通常比较固定,即 O(log e),对于前者,分析起来要复杂得多,在 HFile 中查找正确的数据块是一次时间复杂度为 O(log b) 的运算,找到这一行数据后,再查找列簇里面 keyvalue 对象就是线性扫描过程了(同一列簇的列数据通常是在同一数据块中的),这样的话扫描的时间复杂度是 O(el b), 如果列簇中的列数据不在同一数据块,则需要访问多个连续数据块,这样的时间复杂度为 O(c),因此这样的时间复杂度是两种可能性的最大值,也就是 O(max(c ,el b)
综上所述,查找 Hbase 中某一行的时间开销为:
O(1) 用于查找 region
+O(log e) 用来在 region 中定位 KeyValue,如果它还在 MemStore 中
+O(log b)用来查找 HFile 里面正确的数据块
+O(max(c el b) 用来查找 HFile


【Hbase整体架构剖析】

    推荐阅读