1. 概念相关
为什么有HDFS还要用HBase?
- 延迟: Mapreduce编程框架延迟较高,无法满足大规模数据实时处理应用的需求
- 访问: HDFS面向批量访问而不是随机访问
为什么用HBase而不用传统关系型数据库? / 关系型数据库和HBase有什么区别?
- 数据类型: 传统关系型数据库有丰富的数据类型,HBase把数据存储为未经解释的字符串
- 数据操作: 关系数据库包含了丰富的操作,可涉及复杂的多表连接,而HBase只有简单的插入查询删除清空等,不涉及表与表的连接
- 存储模式: 关系数据库基于行存储,HBase基于列存储
- 数据索引: 关系数据库可构建复杂的多个索引以提高检索效率;HBase只有一个索引:行键(RowKey),经过巧妙地设计,通过rowkey索引或扫描,不会使系统慢下来
- 数据维护: 关系数据库更新会覆盖原值,HBase则是生成一个新的版本,旧数据依然保留
- 可伸缩性: 关系数据库很难实现横向扩展,而HBase能够容易地通过在集群中增加或减少硬件实现性能地伸缩
HBase架构
图1
图2
- 客户端:客户端包含访问HBase的接口,同时在缓存中维护着已经访问过的Region位置信息,用来加快后续数据访问过程
- Zookeeper服务器: 可以帮助选举出一个Master作为集群的总管,并保证在任何时刻总有唯一一个Master在运行,这就避免了Master的“单点失效”问题;保存了记录了META表的位置(元数据入口地址);
-
Master: 主服务器Master主要负责表和Region的管理工作:
–管理用户对表的增加、删除、修改、查询等操作
–实现不同Region服务器之间的负载均衡
–在Region分裂或合并后,负责重新调整Region的分布
–对发生故障失效的Region服务器上的Region进行迁移 - Region服务器 , 负责维护分配给自己的Region,和HDFS交互,Region的拆分(是否拆分由HMaster决定),StoreFile合并,响应用户的读写请求
一些个人理解:
- 一个HBase表, 由多个Region组成,这些Region可以不在一个Region Server上;
- 根据RowKey切分Region,某个或某个连续范围内的RowKey会被切分到一个Region
- 一个Store 对应 一个列族的数据,但一个列族由于Region的切分,可能对应多个store
- 寻址过程客户端只需要询问Zookeeper服务器,不需要连接Master服务器
某个Region Server失效/宕机会怎么处理
某个Region Server宕机,其元数据信息仍存在,其数据在HDFS上(Store File)也存在副本。那么HMaster会根据元数据信息,将该Region Server 管理的数据分给其他Region Server;而Memory Store 中的数据不可恢复,但产生Memory Store数据的操作保存在了HLog中,那么HMaster还需要把HLog拆分,并把对应得HLog分给对应的Region Server。
HBase 读写
读流程
- 客户端向ZK询问元数据所在的Region Server是哪一个
- ZK返回元数据所在的RS
- 客户端向该RS请求元数据
- 返回元数据,客户端根据RowKey和元数据找到要RowKey对应数据所在的RS
- 客户端对该RS发起读请求
- 该RS先去Block Cache中找RowKey对应的数据,如果不存在,再去MemoryStore中找(MemoryStore的数据最新,所以先找该位置),MemroyStore不存在 再去StoreFile找,找到后把该数据刷写到Block Cache中,便于下次直接从缓存中读取,再返回
写流程
Flush 刷写
hbase-default.xml配置文件
- hbase.regionserver.global.memorystore.size: 触发刷写到storefile的整个RegionServer最大内存,默认是堆的40%
- optionalcacheflushinterval: RegionServer中任一Region的MemoryStore时间间隔达到该值,触发刷写,默认1小时
注意 这两个刷写机制会触发整个RegionServer的所有MemoryStore刷写
- hbase.hregion.memstore.flush.size: 单个region的memory store达到某个上限,会触发该memory store刷写,默认128MB
Compacy 合并小文件
因为可能有一些memory store数据量很少的时候被刷写,因此可能存在刷写到磁盘的小文件,这就需要定时进行合并
- hbase.hregion.majorcompaction: 默认是7天,但该操作非常耗资源,因此生产环境下应该关闭,空闲时手动打开
- hbase.hstore.compactionThreshold: 当一个region的storeFile个数超过一定数量,自动进行合并,默认是3
优化相关
预分区
默认的Region拆分方式是每次根据Region个数的平方 * 128MB 作为拆分的最大上限,那么可能导致刚开始的拆分的region很小,后面region很大才会拆分,这样会导致负载不均衡.
所以需要根据业务需求,根据RowKey设置预分区策略
RowKey设计
既然预分区策略和RowKey相关 那么RowKey的设计也至关重要
一般来说,rowkey要保证唯一性,我们都会为它加上一个插入时的时间戳,和一些业务需求常用的字段的值
为了防止数据倾斜,直观的做法是在RowKey前加上一个随机数,再根据这个随机数%预分区数来预分区,保证每个Region大小基本均衡。
但这样带来一个问题就是,当需要查询时,因为我们没办法确定插入数据时的随机数,所以我们需要自定义一个生成哈希值的函数,我们选取一些字段值经过Hash/MD5得到一个数,根据这个数%预分区数,将其添加到RowKey前,将其划分到某个Region。
这样我们需要查询时只需要重复这个过程即可得到其RowKey,再去根据该RowKey读取数据.
除此之外,还要考虑集中性,因为业务有可能是集中读取某一个范围内的数据,那么就要根据这个字段集中存储数据,同时要考虑避免集中带来的数据倾斜
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。