HBase之基本原理!
HBase之基本原理!
月伴飞鱼如何支持海量数据的随机存取
利用
HDFS的分布式存储和Hadoop的分布式计算能力:
- 将数据存储在
HDFS上,并利用Hadoop的MapReduce框架进行分布式计算,从而实现了高可扩展性和高并发性。将数据按照行和列族的方式存储在
HDFS上:
- 这种数据存储方式使得
HBase能够实现高速的随机读写功能。利用了
LSM(Log-Structured Merge-Tree)算法:
- 该算法通过内存和顺序写磁盘的方式,使得随机写入成为可能,同时还能保证读取效率。
支持数据的自动分片和负载均衡:
- 可以支持
PB级别的数据存储和处理,从而满足大规模数据的实时处理需求。
LSM树
LSM树,即日志结构合并树,它是传统关系型数据库的B+树的改进。
LSM树核心就是放弃部分读能力,换取写入的最大化能力。
LSM树会将所有的数据插入、修改、删除等操作保存在内存中,当此类操作达到一定得数据量后,再批量地写入磁盘当中。
- 而在写磁盘时,会和以前的数据做合并。
在合并过程中,并不会像
B+树一样,在原数据的位置上修改,而是直接插入新的数据, 从而避免了随机写。
整体结构
HMaster:
HBase集群的主节点,负责监控RegionServer,处理Region分配和负载均衡。
HRegionServer:
- 管理
Region,处理对所分配Region的IO请求,Region是表的分片,由多个Store组成。
Zookeeper:
- 维护
HBase的运行状态信息,如Region分布信息等。HMaster和RegionServer都依赖Zookeeper。
HRegion:
HBase表的分片,由一个或者多个Store组成,存储实际的表数据。
Store:
Store以Column Family为单位存储数据,主要组成是MemStore和StoreFile(HFile)。- 1个
Column Family的数据存放在一个Store中,一个Region包含多个Store。
MemStore:
内存存储,用于临时存放写数据,达到阈值后刷入
StoreFile。
- 数据会先写入到
MemStore进行缓冲,然后再把数据刷到磁盘。通过内存,也加快了读写速度。
StoreFile(HFile):
- 磁盘上面真正存放数据的文件。
HDFS:
- 用来持久化存储
HFiles。
一个列族就划分成一个
Store,如果一个表中只有 1 个列族,那么每一个Region中只有一个Store。一个
Store里面只有一个MemStore。一个
Store里面有很多个StoreFile,最后数据是以很多个HFile文件保存在HDFS上。
StoreFile是HFile的抽象对象。- 每次
MemStore刷写数据到磁盘,就生成对应的一个新的HFile文件出来。
负载均衡
HBase官方目前支持两种负载均衡策略:
SimpleLoadBalancer策略和StochasticLoadBalancer策略。
SimpleLoadBalancer 策略:
这种策略能够保证每个
RegionServer的Region个数基本相等。假设集群中一共有 n 个
RegionServer,m 个Region,那么集群的平均负载就是average = m/n。虽然集群中每个
RegionServer的Region个数都基本相同。但如果某台
RegionServer上的Region全部都是热点数据,导致 90% 的读写请求还是落在了这台RegionServer上。
- 这样没有达到负载均衡的目的。
StochasticLoadBalancer 策略:
它对于负载的定义不再是
Region个数这么简单,而是由多种独立负载加权计算的复合值,这些独立负载包括:
Region个数,Region负载,读请求数,写请求数,Storefile大小,MemStore大小,数据本地率,移动代价。这些独立负载经过加权计算会得到一个代价值,系统使用这个代价值来评估当前
Region分布是否均衡,越均衡代价值越低。
HBase通过不断随机挑选迭代来找到一组Region迁移计划,使得代价值最小。
Flush机制
MemStore的大小超过某个值的时候,会Flush到磁盘,默认为128M。
MemStore中的数据时间超过1小时,会Flush到磁盘。
HRegionServer的全局MemStore的大小超过某大小会触发Flush到磁盘,默认是堆大小的40%。
Compact机制
HBase需要在必要的时候将小的Store File合并成相对较大的Store File,这个过程为Compaction。
- 为了防止小文件过多,以保证查询效率。
在
HBase中主要存在两种类型的Compaction合并。
Minor Compaction小合并:
- 在将
Store中多个HFile合并为一个HFile。- 这个过程中,达到
TTL(记录保留时间)会被移除,删除和更新的数据仅仅只是做了标记,并没有物理移除。
- 这种合并的触发频率很高。
Major Compaction大合并:
合并
Store中所有的HFile为一个HFile。这个过程有删除标记的数据会被真正移除,同时超过单元格
maxVersion的版本记录也会被删除。合并频率比较低,默认7天执行一次,并且性能消耗非常大,建议生产关闭(设置为0),在应用空闲时间手动触发。
- 一般可以是手动控制进行合并,防止出现在业务高峰期。
Region拆分机制
Region中存储的是大量的Rowkey数据,当Region中的数据条数过多的时候,直接影响查询效率。
- 当
Region过大的时候,HBase会拆分Region。
HBase的Region Split策略一共有以下几种。
ConstantSizeRegionSplitPolicy:
0.94版本前默认切分策略。当
Region大小大于某个阈值之后就会触发切分,一个Region等分为2个Region。
- 在生产线上这种切分策略有相当大的弊端:切分策略对于大表和小表没有明显的区分。
阈值设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个。
如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的
Region,这对于集群的管理、资源使用、Failover不好。
IncreasingToUpperBoundRegionSplitPolicy:
0.94版本~2.0版本默认切分策略。总体看和
ConstantSizeRegionSplitPolicy思路相同,一个Region大小大于设置阈值就会触发切分。
- 但这个阈值并不是一个固定的值。
- 而是会在一定条件下不断调整,调整规则和
Region所属表在当前RegionServer上的Region个数有关系。
Region Split的计算公式是:
RegionCount^3 * 128M * 2,当Region达到该size的时候进行split。例如:
第一次split:
1^3 * 256 = 256MB第二次split:
2^3 * 256 = 2048MB第三次split:
3^3 * 256 = 6912MB第四次split:
4^3 * 256 = 16384MB > 10GB,取较小的值10GB后面每次split的size都是10GB了。
SteppingSplitPolicy:
2.0版本默认切分策略。
依然和待分裂
Region所属表在当前RegionServer上的Region个数有关系。如果
Region个数等于1,切分阈值为flush size * 2,否则为MaxRegionFileSize。这种切分策略对于大集群中的大表。
小表会比
IncreasingToUpperBoundRegionSplitPolicy更加友好,小表不会再产生大量的小Region,而是适可而止。
KeyPrefixRegionSplitPolicy:
根据
RowKey的前缀对数据进行分组,这里是指定RowKey的前多少位作为前缀,比如RowKey都是16位的,指定前5位是前缀。那么前5位相同的RowKey在进行
region split的时候会分到相同的Region中。
DelimitedKeyPrefixRegionSplitPolicy:
保证相同前缀的数据在同一个
Region中,例如RowKey的格式为:userid_eventtype_eventid,指定的delimiter为_。则split的的时候会确保userid相同的数据在同一个
Region中。
DisabledRegionSplitPolicy:
不启用自动拆分,需要指定手动拆分。
预分区
当一个
table刚被创建的时候,HBase默认的分配一个Region给table。
这时所有的读写请求都会访问到同一个
RegionServer的同一个Region中。这个时候就达不到负载均衡的效果了,集群中的其他
RegionServer就可能会处于比较空闲的状态。解决办法:
- 可以用预分区(
pre-splitting),在创建table的时候就配置好,生成多个Region。如何预分区?
- 每一个
Region维护着startRow与endRowKey。- 如果加入的数据符合某个
Region维护的RowKey范围,则该数据交给这个Region维护。
手动指定预分区:
1 | create 'person','info1','info2',SPLITS => ['1000','2000','3000','4000'] |
Region定位
HBase支持put,get,delete和scan等基础操作,所有这些操作的基础是region定位。
region 定位基本步骤:
客户端与
ZooKeeper交互,查找hbase:meta系统表所在的Regionserver。
hbase:meta表维护了每个用户表中rowkey区间与Region存放位置的映射关系,具体如下:
rowkey:table name,start key,region id。
value:RegionServer对象(保存了RegionServer位置信息等)。客户端与
hbase:meta系统表所在RegionServer交互,获取rowkey所在的RegionServer。客户端与
rowkey所在的RegionServer交互,执行该rowkey相关操作。需要注意:
客户端首次执行读写操作时才需要定位
hbase:meta表的位置。之后会将其缓存到本地,除非因
region移动导致缓存失效,客户端才会重新读取hbase:meta表位置,并更新缓存。
读写流程
读操作:
首先从
ZooKeeper找到meta表的region位置,然后读取hbase:meta表中的数据。
hbase:meta表中存储了用户表的region信息。根据要查询的
namespace、表名和rowkey信息,找到写入数据对应的Region信息。找到这个
Region对应的RegionServer,然后发送请求。查找对应的
Region。先从
MemStore查找数据,如果没有,再从BlockCache上读取。
HBase上RegionServer的内存分为两个部分:
- 一部分作为
MemStore,主要用来写。- 另外一部分作为
BlockCache,主要用于读数据。如果
BlockCache中也没有找到,再到StoreFile(HFile)上进行读取。
- 从
StoreFile中读取到数据之后,不是直接把结果数据返回给客户端。- 而是把数据先写入到
BlockCache中,目的是为了加快后续的查询,然后在返回结果给客户端。
写操作:
首先从
ZooKeeper找到hbase:meta表的Region位置,然后读取hbase:meta表中的数据。
hbase:meta表中存储了用户表的Region信息。根据
namespace、表名和rowkey信息找到写入数据对应的Region信息。找到这个
Region对应的RegionServer,然后发送请求。把数据分别写到
HLog (WriteAheadLog)和MemStore各一份。
MemStore达到阈值后把数据刷到磁盘,生成StoreFile文件。删除
HLog中的历史数据。
BulkLoad机制
用户数据位于
HDFS中,业务需要定期将这部分海量数据导入HBase系统,以执行随机查询更新操作。这种场景如果调用写入
API进行处理,极有可能会给RegionServer带来较大的写人压力。
引起
RegionServer频繁flush,进而不断compact、split,影响集群稳定性。引起
RegionServer频繁GC,影响集群稳定性。消耗大量
CPU资源、带宽资源、内存资源以及IO资源,与其他业务产生资源竞争。在某些场景下,比如平均
KV大小比较大的场景,会耗尽RegionServer的处理线程, 导致集群阻塞。所以
HBase提供了另一种将数据写入HBase集群的方法:BulkLoad。
BulkLoad首先使用MapReduce将待写入集群数据转换为HFile文件,再直接将这些HFile文件加载到在线集群中。
BulkLoad没有将写请求发送给RegionServer处理,可以有效避免上述一系列问题。


















