1 概念
HBase是Apache的Hadoop项目的子项目,是Hadoop Database的简称。构建在 Apache Hadoop和 Apache ZooKeeper之上。
一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价的机器上搭建起大规模结构化存储集群。
HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。
特点:
- 海量存储:HBase适合存储PB级别的海量数据,在PB级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。这与HBase的记忆扩展性息息相关。正是因为HBase的良好扩展性,才为海量数据的存储提供了便利。
- 列式存储:列式存储,HBase是根据列族来存储数据的。列族下面可以有非常多的列,列族在创建表的时候就必须指定,而不用指定列。
- 极易扩展:HBase的扩展性主要体现在两个方面,一个是基于上层处理能力(RegionServer)的扩展,一个是基于存储能力(HDFS)的扩展。
- 高并发:目前大部分使用HBase的架构,都是采用廉价PC,因此单个IO的延迟其实并不小,一般在几十到上百ms之间。这里说的高并发,主要是在并发的情况下,HBase的单个IO延迟下降并不多。
- 稀疏:稀疏主要是针对HBase列的灵活性,在列族中,可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间
概括:
- HBase底层存储并非必须是HDFS文件系统,但是HDFS是最佳选择
- 该技术来源于Google论文“Bigtable:一个结构化数据的分布式存储系统”。就像Bigtable利用了Google文件系统(File System)所提供的分布式数据存储一样,HBase在Hadoop之上提供了类似于Bigtable的能力
- 一个高可靠性、高性能、面向列、可伸缩的分布式存储系统。Hbase参考了谷歌的BigTable建模,使用HDFS作为底层存储,使用Zookeeper作为协同服务组件。
- 安装Hbase必须安装jdk,Hbase是Java编写
2 数据模型
Namespace(表命名空间):表命名空间不是强制的,如果想把多个表分到一个组去统一管理的时候才会用到表命名空间。
Table(表):一个表由多行组成。
Row(行):一个行键和一个或多个列组成
Column Family(列族):列族是多个列的集合。
Column Qualifier(列):列由 列族和 列限定符组成,列是可以随意定义的,一个行中的列不限名字,不限数量,只限定列族。
如:
content:pdf
content是列族,pdf是列限定符。
Cell(单元格):单元格是行,列族和列限定符的组合,它包含一个值和一个时间戳,时间戳表示值的版本。
Timestamp(时间戳/版本号):用来标定同一个列中多个Cell的版本号。当在插入数据的时候,如果不指定版本号,系统会自动采用系统的当前时间戳来作为版本号,也可以手动指定一个数字作为版本号。
Rowkey(行键):用来标识表中唯一的一行数据,以字节数组形式存储,类似关系型数据库中表的主键。rowkey在HBase中时严格按照字典序排序的。
时间戳按降序存储,取模个cell的值默认取版本最新的值。取列中不存在的值将不返回任何值。
3 架构
3.1 主从结构
Master:
- 负责RegionServer的Region分配和负载均衡。
- 发现失效的RegionServer并重新分配Region;
- 处理Schema更新请求(表的创建、删除、修改、列族的增加等)。
- 管理namespace和table的元数据(实际存储在HDFS上)。
RegionServer:
- 存放和管理本地Region。
- 读写HDFS,管理Table中的数据。
- 处理Client的IO请求(Client从Master中获取元数据,找到RowKey所在的Region/RegionServer后,类HDFS)。
- 负责region的切分和合并
- HDFS
- 为HBase提供最终的底层数据存储服务(包括元数据和表数据)
- 同时为HBase提供高可用(HLog)的支持
- Zookeeper
- 为HBase提供Failover机制,选举master,避免master单点故障问题;
- 存储所有Region的寻址入口,保存
hbase:meta
表信息; - 实时监控RegionServer的状态,将RegionServer的上线和下线信息实时通知给master;
- 存储HBase的Schema,包括有哪些Table,每个Table有哪些Column Family
Client
Client使用HBase的RPC机制与HMaster、RegionServer进行通信,Client与Master进行管理类通信,与RegionServer进行数据操作类通信。Client包含了访问HBase的接口,另外Client还维护了对应的cache来加速HBase的访问,
3.2 RegionServer结构
region, 可以理解为一张表或者一张表的横向切分后的一段,Hbase会自动将表切分成多个region, 初始时一张表只有一个region。
一个region包含某些Strore, 一个store就相当于一个列族
一个列族里面有一个Memstore和零个或多个StoreFile,当写入数据时,首先写入MemStore中(先这样理解),当Memstore中的数据达到一定程度时,会flush到一个Storefile(存储在Hfile)
当一个Strore中的Storefile数量达到一定程度时,就会进行StoreFile的合并操作。
当region中总的StoreFile达到一定体量后,便会触发切分region的操作。将region切分成两个子region,然后由HMaster根据负载情况分配到HRegionServer中。
WAL预写日志,在将数据写入MemStore前,会先写入WAL中,再进行前面的第二部操作。这样的好处是当Region所在的机器宕机后,可以从WAL中恢复数据。(在写入MemStore中会对数据根据rowKey进行排序操作)
BlockCache,读缓存,用于在内存中缓存经常被读的数据。Least Recently Used (LRU) 数据在存满时会被失效。
4 原理
4.1 region合并
4.1.1 小合并(Minor Compaction)
当MemStore达到hbase.hregion.memstore.flush.size
大小的时候会将数据刷写到磁盘,生成StoreFile。随着业务的发展,数据量会越来越大,会产生很多的小文件,对于HBase的数据读取,如果要扫描大量的小文件,会导致性能很差,因此需要将这些小文件合并成一个大一点的文件。
小合并,就是把多个小的StoreFile组合在一起,形成一个较大的StoreFile,步骤如下:
- 分别读取出待合并的StoreFile文件的KeyValues,并顺序地写入到位于
/hbase/.tmp
目录下的临时文件中; - 将临时文件移动到对应的Region目录中;
- 将合并的输入文件路径和输出路径封装成KeyValues写入WAL日志,并打上compaction标记,最后强制执行sync;
- 将对应region数据目录下的合并的输入文件全部删除,合并完成。
小合并一般速度比较快,对业务的影响也比较小。本质上,小合并就是使用短时间的IO消耗以及带宽消耗换取后续查询的低延迟。在Minor Compaction过程中,达到TTL(记录保留时间)的数据会被移除,但是由墓碑标记的记录不会被移除,因为墓碑标记可能存储在不同HFile中,合并可能会跨过部分墓碑标记。
4.1.2 大合并(Major Compation)
大合并就是将一个Region下的所有StoreFile合并成一个大的StoreFile文件。在大合并的过程中,之前删除的行和过期的版本都会被删除。大合并一般一周做一次,由hbase.hregion.majorcompaction
参数控制。大合并的影响一般比较大,尽量避免同一时间多个Region进行合并,因此HBase通过hbase.hregion.majorcompaction.jitter
参数来进行控制,用于防止多个Region同时进行大合并。
具体算法:
hbase.hregion.majorcompaction
参数的值乘以一个随机分数,这个随机分数不能超过hbase.hregion.majorcompation.jitter
的值(默认为0.5)。
通过hbase.hregion.majorcompaction
参数的值加上或减去hbase.hregion.majorcompaction
参数的值乘以一个随机分数的值就确定下一次大合并的时间区间。
可以通过hbase.hregion.majorcompaction
设置为0来禁用major compaction。
4.2 Client-Server交互逻辑
- 客户端首先会根据配置文件中zookeeper地址连接zookeeper,并读取/<hbase-rootdir>/meta-region-server节点信息,该节点信息存储HBase元数据(hbase:meta)表所在的RegionServer地址以及访问端口等信息。用户可以通过zookeeper命令(get /<hbase-rootdir>/meta-region-server)查看该节点信息。
- 根据hbase:meta所在RegionServer的访问信息,客户端会将该元数据表加载到本地并进行缓存。然后在表中确定待检索rowkey所在的RegionServer信息。
- 根据数据所在RegionServer的访问信息,客户端会向该RegionServer发送真正的数据读取请求。服务器端接收到该请求之后需要进行复杂的处理,具体的处理流程将会是这个专题的重点。
- 客户端只需要配置zookeeper的访问地址以及根目录,就可以进行正常的读写请求。不需要配置集群的RegionServer地址列表。
- 客户端会将hbase:meta元数据表缓存在本地,因此上述步骤中前两步只会在客户端第一次请求的时候发生,之后所有请求都直接从缓存中加载元数据。如果集群发生某些变化导致hbase:meta元数据更改,客户端再根据本地元数据表请求的时候就会发生异常,此时客户端需要重新加载一份最新的元数据表到本地。
region寻址步骤:
- Client向zookeeper获取存储元数据的 hbass:meta 表 所在的 RegionServer A
- 连接 RegionServer A,将habse:meta表缓存到本地,根据rowKey获取region,根据region获取存取该region的 RegionServer B
4.3 Hbase写流程
- Client进行region寻址得到ReionServer的信息
- Client连接RegionServer并发起 put请求
- 写入WHL,写入HLog,写入MemStore中
- 向Client反馈信息
- 判断MemSrore中的量是否达到
flush成为一个StoreFile
的阈值
写入Hlog的目的:
在这个过程中系统会在Zookeeper中记录一个checkpoint,表示这个时刻之前的更新已经持久化了,当系统出现意外时,可能导致MemStore中的数据丢失,此时使用HLog来恢复chckpoint之后的数据。
在对HBase进行写操作的时候,进行Put和Update操作的时候,其实是新增了一条数据,即使是在进行Delete操作的时候,也是新增一条数据,只是这条数据没有value,类型为DELETE,这条数据叫做墓碑标记(Tobstone)。数据的真正删除是在compact操作时进行的。
4.4 Hbase读流程
- Client进行region寻址得到ReionServer的信息
- Client连接RegionServer并发起 get 请求
- 先从
BlockCache
中查找数据,找不到再去MemStore
和StoreFile
中查询数据
4.5 RegionServer故障恢复
心跳机制:
在Zookeeper中保存着RegionServer的相关信息,在RegionServer启动的时候,会在Zookeeper中创建对应的临时节点。RegionServer通过Socket和Zookeeper建立session会话,RegionServer会周期性的向Zookeeper发送ping消息包,以此说明自己还处于存活状态。而Zookeeper收到ping包后,则会更新对应Session的超时时间。
当Zookeeper session超时时间还未收到RegionServer的ping包,则Zookeeper会认为该RegionServer出现故障,Zookeeper会将该RegionServer对应的临时节点删除出,并通知Master,Master收到RegionServer挂掉的信息后就会启动数据恢复流程。