👻HBase详解
🎈一、HBase优点
- 容量大:
Hbase单表可以有百亿行、百万列,数据矩阵横向和纵向两个维度所支持的数据量级都非常具有弹性- 面向列:
面向列的存储和权限控制,并支持独立检索,可以动态增加列,即,可单独对列进行各方面的操作列式存储,其数据在表中是按照某列存储的,这样在查询只需要少数几个字段的时候,能大大减少读取的数量- 多版本:
Hbase的每一个列的数据存储有多个Version,比如住址列,可能有多个变更,所以该列可以有多个version- 稀疏性:
为空的列并不占用存储空间,表可以设计的非常稀疏。不必像关系型数据库那样需要预先知道所有列名然后再进行null填充- 拓展性:
底层依赖HDFS,当磁盘空间不足的时候,只需要动态增加datanode节点服务(机器)就可以了- 高可靠性:
WAL机制,保证数据写入的时候不会因为集群异常而导致写入数据丢失Replication机制,保证了在集群出现严重的问题时候,数据不会发生丢失或者损坏Hbase底层使用HDFS,本身也有备份。- 高性能:
底层的LSM数据结构和RowKey有序排列等架构上的独特设计,使得Hbase写入性能非常高。
Region切分、主键索引、缓存机制使得Hbase在海量数据下具备一定的随机读取性能,该性能针对Rowkey的查询能够到达毫秒级别,LSM树,树形结构,最末端的子节点是以内存的方式进行存储的,内存中的小树会flush到磁盘中(当子节点达到一定阈值以后,会放到磁盘中,且存入的过程会进行实时merge成一个主节点,然后磁盘中的树定期会做merge操作,合并成一棵大树,以优化读性能。)LSM树的介绍
🎈二、HBase读写流程
1、公共读写流程解析
大致流程图如下,后面分别对0.96之前和之后版本进行文字赘述😴
① HBase0.96版本之前
首先无论是读还是写都先去zookeeper中获取-root-(-root-文件单独存放在一个HRegion中)文件所在HRegionServer的位置信息,而-root-表中存放的是.meta.文件在HRegion中的映射位置,因为.meta.文件也是需要依RowKey进行切分,分别存放在HRegion中。.meta.文件中存放则RowKey与HRegion和HRegion与HRegionServer的映射关系,这样无论是读写,都可以通过RowKey的映射关系找到存放着文件的多个HRegion的位置。
② HBase0.96版本之后
0.96版本之后,取消了-root-文件,直接将-root-文件里的映射信息直接存放到zookeeper中。
2、读流程详解
① 客户端先访问zookeeper,获取meta文件所在的HRegionServer(BlockCache公共区域,可以设置)的位置
② 访问Meta对应的HRegionServer,加载meta加载到内存,在通过查询的RowKey获取对应HRegion的映射关系
③ 获取映射关系后,进行数据请求,由于数据分在多个HRegion所以对应创建多个HRegionScanner通道进行检索数据。
④ 检索先从MemStore中检索,失败后在去StoreFile(内部有序外部无序)中检索,最后经过检索过滤合并,获得最后的keyValue。
3、写流程详解
① 首先客户端会与HRegionServer建立连接。
② 会将写入操作写入到HLog日志中(WAL机制),在进行具体的写入操作至store(MemStore中)。
③ MemStore默认128M,一个store对应一个列簇,一旦满了,就会申请新的MemStore,而旧的则会被HRegionServer启动flushCache刷到硬盘(HDFS)。
④ 刷到硬盘上形成的文件变成StoreFile文件(底层格式是HFile),当SF文件越来越多,则会进行合并,合并成一个大的StoreFile文件,相邻的StoreFile也会启动合并。
🎈三、HBase刷写
1、HBase数据刷写的时机
- MemStore的阈值128M,但内存占用达到512M时阻塞客户端的写入(
MemStore会堵塞
)- HRegionServer总内存的阈值,总内存*40%*95%开始刷写(当达到flush级别则
HRegionServer
整个会堵塞)- Wal日志机制的阈值,也就是在某种情况下,日志越来越多,也就意味着MemStore中为持久化的数据越来越多,导致HRegionServer挂掉,所以在Wal达到一定数量进行刷出
- 自定义刷写的时间间隔
- 手动刷写=命令刷写
2、HBase刷写策略
hbase1.1版本
此时的MemStore的刷写级别是HRegion的,也就是说一个MemStore要刷写,那么整个HRegion中的其他MemStore都要一起刷写(所以列簇不要太多不要超过三个)。hbase2.x版本
还是对HRegion中全部的MemStore进行刷写,不过设置了阈值的判断,大于阈值的将会被刷写。
3、HBase刷写流程
prepareFlush 阶段
:
准备刷出阶段,会将MemStore文件创建一个snapshot,为了在创建snapshot时写入造成错误,所以在创建snapshot时携带一个updateLock锁,创建snapshot很快,不受影响。flushCache 阶段
:
判断快照是否有问题,返回result=null没问题则进行刷出缓存和提交即可。
🎈四、HBase合并
1、HBase数据合并(Compaction)
Minor Compaction
将小的相邻的FileStore进行合并,但不会对已经失效的数据进行删除,会对超过TTL(生存期的数据进行处理),减少小数据Major Compaction
将所有的FileStore进行合并,并对三无数据进行处理,速度较慢占用大量资源,对上层应用造成影响,一般关闭此功能,都是手动开启(清理三类无意义数据:被删除的数据、TTL过期数据、版本号超过设定版本号的数据。)
2、合并时机
Memstore Flush
Store的StoreFile文件树进行判断,判断是否达到合并的阈值(文件数量为10个)周期性检测
默认定义一个合并的周期,如果达到阈值也会检查文件的数目,如果文件超过3个—小合并,不超过三个,检查最早一次合并的时间,超过七天会进行大合并手动触发
在低峰期的时候手动开启major compaction进行Filestore的全部合并与数据清理
3、合并策略
选择线程
默认值为2 * maxFlilesToCompact * hbase.hregion.memstore.flush.size
超过则用longcompaction
,处理小规模用Sortcompaction
Minor合并
HBase 主要有两种 minor
策略: RatioBasedCompactionPolicy (0.96.x之前)和ExploringCompactionPolicy(当前默认)
①RatioBasedCompactionPolicy
(基于比列的合并策略):比较大一些的都不会进行合并
②ExploringCompactionPolicy
策略(默认策略)
🎈五、数据切分(Region Split)
1、切分原因
- ①
数据分布不均匀
导致一个HRegion中分配了很多请求,导致效率大大减低- ②
compaction性能损耗严重
主要用于排序合并,一旦文件过大,导致占用大量资源,并且处理较慢,导致资源损耗严重- ③
资源耗费严重
HBase每天的写入量很大,如果不进行数据切分存储,很快单点资源就会被占用耗尽
2、切分时机
当每次的数据合并后,对应Region会有一个requestSplit请求,会首先检查filesize是否达到阈值,超过进行切分
- ① ConstantSizeRegionSplitPolicy( 0.94版本)
也就HRegion内达到10G- ② IncreasingToUpperBoundRegionSplitPolicy:
如果store大小大于一个变化的阀值就允许split
3、切分流程⭐
- ① 寻找切点
首先寻找切点,先遍历HRegion中的FileStore文件,找到最大的FileStore文件,在从中找到最大的Rowkey数据,作为切点
HBase将整个切分过程包装成了一个事务,意图能够保证切分事务的原子性- ② 三个阶段
prepare准备阶段
先创建连个子HRegion(具体是创建了HRegionInfo对象,里面存放表名,HRegion名等),准备存放切分后的数据,还会创建一个transaction journal,进行记录切分的进展
execute执行阶段
首先HRegionServer会更改ZK中HRegion节点的状态为Spliting,此时HMaster就会检查到HRegion的状态,便会修改内存中HRegion的状态,子HRegion会在父目录下创建一个.split临时目录存放子HRegion的信息,父HRegion关闭写入,将数据写出持久化,此后这段时间访问父HRegion的请求都会抛出异常,核心分裂步骤:在.split文件夹下新建两个子文件夹,称之为daughter A、daughter B,并在文件夹中生成reference文件,分别指向父region中对应文件。最后父HRegion将两个子HRegion移到HBase根目录下开始服务,待子HRegion工作则删除父HRegion
rollback回滚阶段
如果execute阶段出现异常,则执行rollback操作。为了实现回滚,整个切分过程被分为很多子阶段,回滚程序会根据当前进展到哪个子阶段清理对应的垃圾数据。
🎈六、表设计
1、行健设计
① 长度原则
Rowkey 是一个二进制码流,最大长度是64KB,建议越短越好。10-100长度即可,不要超过 16 个字节。(占用资源)② 散列原则
尽量将连续数据存放到更多的RegionServer(不然容易RowKey堆积到一个节点上)③ 唯一原则
必须在设计上保证其唯一性④ 热点数据
RowKey反转,加演算法,Hash算法都可以解决(就是数据堆积)RowKey反转 xxx01 xxx02 02xxx 01xxx ,或者加盐,进行平均分配,也可加随机数但不好查找
2、列簇设计
- 尽可能少和短
- 列簇HFIle块默认是64KB,数据库的大小影响数据库的索引的大小