HBase

Hbase 基础知识
一、数据模型
Name Space 
	命名空间,类似于关系型数据库database的概念,每一个命名空间下面有多个表。
	Hbase有两个自带的命名空间,分别是hbase 和 default,hbase 中存放的是Hbase的内置表;default是用户默认使用的命名空间。
Table
	Table 类似于关系型数据库表的概念。与关系型数据库中的表不同的是,Hbase 定义表只需要声明列族即可,不需要声明具体的列。
	这就意味着,往Hbase 写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,Hbase能够轻松应对字段变更的场景。
Row
	Hbase 表中的每行数据都是由一个RowKey 和 多个 Column 组成,数据是按照RowKey的字段顺序存储的,并且查询时只能根据Rowkey进行检索,所以RowKey的设计十分重要。
Column
	Hbase中的每列都是由Column Family (列族)Column Qualifiler(列限定符) 进行限定。例如 info:name、info:age。
	建表时,只需要指明列祖,而列限定符无需预先定义。
Time Stamp 	
	用于标识数据的不同版本(version),每条数据写入时,系统都会自动为其加上该字段,其值为写入Hbase 的时间。
Cell{rowkey,column Family, column Qualifier,time Stamp} 唯一确定的单元。cell 中的数据没有类型,全部是以字节码的形式存储。 
二、Hbase 的基本架构

img

Region Server 
	Region Server作为 Region 的管理者,其主要实现类为HRegionServer,主要作用如下:
	对数据的操作:get,put,delete
	对Region的操作:splitRegion、CompactRegion
Master
	Master 作为 Region Server 的管理者,其实现类为HMaster,主要作用如下:
	对表的操作:Create、delete、alter
	对RegionServer的操作:为RegionServer分配Region,监控每个RegionServer 的状态,负责RegionServer负载均衡和故障转移 
ZooKeeper 
	Hbase 通过ZooKeeper来做Master的高可用、RegionServer 的监控、元数据的入口以及集群的配置维护工作。
这使得HMaster不再是单点故障。可以使用HA通过选举,保证任何时候,集群中只有一个活着的HMasterHMasterRegionServers 启动时会向ZooKeeper注册。
HDFS
	HDFS 为 Hbase 提供最终的底层的数据存储服务,同时为HBase 提供高可用支持。
三、HBase的详细架构

在这里插入图片描述

HMaster:相当于HBase的大脑,充当Region Server 的管理者。
  当HRegionSrever中存储的数据表过大以后,HMaster通知HRegionSrever对表进行切割,实现集群的负载均衡;HRegionSrever故障失效时,HMaster负责此节点上所有数据的迁移;
  同时整个HBase的数据读写操作都是通过HMaster进行管理和通知的;    
ZooKeeper:实现HBase的高可用,它保证集群中只有一个HMaster工作,同时还监视HRegionServer的工作状态,当HRegionServer处理客户端的数据读写出现异常时,ZooKeeper会通知HMaster进行处理。
HRegionServer:是HBase的核心组件,负责执行HBase的所有数据读写操作,HRegionSrever包含了:HLogHRegionStore等组件。
HRegion:是HRegionServer中存储数据的组件,一张数据表会被周期切分,所以一张完整的数据表可能会对应多个HRegion,而HRegion又由多个Store组成。
StoreStore也是存储数据的核心组件,它内部包含Mem StoreStoreFile两个组件,前者以内存形式存储数据,后者以HDFS文件形式存储数据。
Mem Store:是数据存储的首选方式,当内存空间满了后,HBase会将内存的数据一次性刷写到HDFS上,以文件形式存储,也就是StoreFile中,空间大小并不是刷写数据的唯一条件,当数据在内存中存储时间达到设定时间时,HBase也会进行数据刷写操作。
StoreFile:是存储数据的文件形式,基于HDFS存储,StoreFileHFile 的形式存储在HDFS上。每个Store会有一个或多个StoreFileHFile),数据在每个StoreFile中都是有序(局部有序)的。
Meta表:用于保存集群中HRegions的位置信息(region列表)。ZooKeeper存储着Meta表的位置。
HLog:保证了HBase的可靠性,它是记录HRegionSrever的数据读写等操作的编辑日志,当HRegionSrever发生故障时,HMaster接收到ZooKeeper的通知后,可以通过HLog对数据进行恢复。
四、Region Server 的架构

img

MemStore 
	MemStroe 是存放在内存中。保存修改的数据即 KeyValues。当MemStore的大小达到一定的阈值(默认为64M),MemStore就会被Flush到文件中,即生成一个快照文件。目前Hbase会有一个线程负责MemStore的flush 操作,即写缓存。
	由于HFile 中的数据要求是有序的,所以数据现存储在MemStore中,排好序后,等到达刷写时机才会刷写到HFile。每次刷写都会生成一个新的HFile文件。
StoreFile
	MemStore内存中的数据写入到文件后就是StoreFile[MemStore每次Flush 操作都会生成一个StoreFile]StoreFile 底层是以Hfile 的格式存储的。每个Store 会有一个或者多个StoreFile[HFile],数据在StoreFile中都是有序的
WAL
	由于数据要经过MemStore排序后才能刷写到HFile,但是把数据保存在内存中会有很高的概率导致数据丢失。
为了解决这个问题,数据会先写入到一个叫Write-Ahead-log 文件中,然后再写入到MemStore中。所以系统出现故障的时候,数据可以通过日志文件重建。
BlockCache
读缓存,每次查询的数据都会被缓存到BlockCache,方便下次查询
Region/Store/StoreFile/Hfile之间的关系

(1)Region
	Table在行的方向上分隔成多个Region,RegionHbase中分布式存储和负载均衡的最小单位,即不同的Region分别在不同的RegionServer上,但是同一个Region是不会被拆分成多个Server上。
	Region 是按照大小分隔的,表中的每一行只能属于一个Region。随着数据不断插入到表中,Region不断增大,当region 中某一列族达到一个阈值(默认256M)时就会被分成两个Region.

(2)Store 
一个Region 由一个或者多个Store组成,至少是一个Store.
Hbase会把一起访问的数据放到一个Store,即为每个ColumnFamily 建一个Store [即有几个ColumnFamily,也就有几个Store]
一个Store由一个MemStore0 或者多个StoreFile.

(3)MemStore 
MemStore存放在内存中,保存修改的数据即KeyValues。
当MemStore的大小达到一个阈值时(64M),MemoStore 会被flush 到一个文件,即生成一个快照,目前Hbase会有一个线程负责MemStoreFlush 操作。

(4)StoreFile 
MemStore 内存中的数据写入到文件后就是StoreFile[MemStore 每次Flush 操作都会生成一个新的StoreFile]StoreFile底层是以HFile的格式存储的。

(5)HFile 
HFileHbaseKeyValue 数据的存储格式,是Hadoop的二进制文件。一个StoreFile 对应一个HFile,HFile 存储在HDFS 上面。
五、逻辑架构&物理架构

在这里插入图片描述
在这里插入图片描述

六、读写数据流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5N4PiDRh-1637938991887)(C:\Users\86156\AppData\Local\YNote\data\weixinobU7Vjlm8ZJz94-Ski7f9_r4aMUU\e7027e6f42304ec7a19bd05f27c2a175\clipboard.png)]

写数据
1Client 先从缓存中定位region,如果没有缓存则需访问zookeeper获取 hbase:meta 表位于哪个 Region Server2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,
查询出目标数据位于哪个 Region Server 中的哪个 Region 中。(找到小于rowkey并且最接近rowkey的startkey对应的region)
并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)将数据顺序写入(追加)到 WAL;
5)将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
6)向客户端发送 ack;
7)等达到 MemStore 的刷写时机后,将数据刷写到 HFile

在这里插入图片描述

读数据
1Client 先从缓存中定位region,如果没有缓存则需访问zookeeper获取 hbase:meta 表位于哪个 Region Server2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)分别在 Block Cache(读缓存),MemStoreStore FileHFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
5) 将从文件中查询到的数据块(BlockHFile 数据存储单元,默认大小为 64KB)缓存到Block Cache6)将合并后的最终结果返回给客户端
七、HLog
HBase的数据写入并不是直接写入到数据文件中,而是先写入缓存(MemStroe,当满足一定的条件下缓存中的数据再会异步刷新到磁盘。
为了防止数据写入缓存之后不会因为RegionServer进程发生异常而导致数据丢失,在写入缓存之前需要将数据顺序写入到HLog中。如果RegionServer 发生宕机或者其他的异常,这种设计可以从HLog中进行日志回放进行数据的补救,保证数据的不丢失。
HLog 的生命周期包括:HLog构建、HLog滚动、HLog失效、HLog删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PWRCSB7m-1637938991889)(C:\Users\86156\AppData\Local\YNote\data\weixinobU7Vjlm8ZJz94-Ski7f9_r4aMUU\444d88ddd2464ccb82ec80a555765664\h1.png)]

HLog构建:
	Hbase中,WAL的实现类为HLog。每一个RegionServer拥有一个HLog日志,该RegionServer中所有Region的写入都会写入到同一个HLog。
日志数据的最小单元为<HLogKey,WALEdit>,HLogKey 主要存储了log sequence number[日志序列号],write time [写入时间],region name,table name[表名]以及cluster ids。
log sequence number是一次行级事务的自增序号,是HLog的一个重要的元数据,和HLog的生命周期息息相关。[行级事务是什么?简单来说,就是更新一行中的多个列族,多个列,行级事务能够保证这次更新的原子性、一致性、持久性、隔离性。]  sequenceid是region级别的自增序号。每个region都维护属于自己的sequenceid,不同region的sequenceid相互独立。
region name 和 table name 分别表示该段日志属于哪个region的哪张表。
cluster ids 用于表示将日志复制到集群中的其他机器上。

WALEdit用来表示一个事务中的更新集合,一次行级事务可以原子操作同一行中的多个列。上图中WALEdit包含多个KeyValue

在这里插入图片描述

HLog 滚动
	HBase后台启动了一个线程会每隔一段时间(由参数’hbase.regionserver.logroll.period’决定,默认1小时)进行日志滚动,即新生成一个新的日志文件。
可见,HLog 并不是一个大文件,而是会生成很多的小文件。其原因在于:
随着数据的不断写入,HLog 所占的空间会越来越大,然而很多的日志已经没有任何作用,这一部分数据完全可以被删除掉。删除数据的较好的方式即为一个文件一个文件的删除。因此设计了日志滚动机制。
Hlog 失效
	上文提到,很多日志文件失效而被删除掉,并且删除是以文件为单元执行的。
从原理上来说,一旦数据从MemStore中落盘,对应的日志文件就可以被删除。因此一个文件中所有数据失效,只需要看该文件中所有的Region 对应的最大的Sequenceid 对应的数据是否已经落盘就可以。同时Hbase 在执行Flush 操作时会记录对应的最大Sequenceid,如果HLog 中最大的SequenceId 小于 FLush 刷新操作对应的SequenceId 就可以认为该文件失效。一旦该文件失效,就可以将该文件从WALS 文件夹移动到OLdWALS.
HLog 删除
	HMaster后台会启动一个线程每隔一段时间(由参数’hbase.master.cleaner.interval’,默认1分钟)会检查一次OldWALs下所有的日志文件,确认是否可以被删除。确认之后执行删除操作。
之所以要确认:第一对于使用Hlog进行主从复制的业务来说,第三步的确认并不是完整的,需要继续确认该HLOG 是否还在应用于主从复制;第二对于没有执行主从复制的业务来讲,HBase依然提供了一个过期的TTL(由参数’hbase.master.logcleaner.ttl’决定,默认10分钟)也就是说OldWALs里面的文件最多依然再保存10分钟。
八、Region 切分策略
	RegionServerRegion的数量取决于MemStore内存的使用,每个Region 拥有一组MemStore[memStore的数量由Store决定,Store的数量由建表时的指定的列族个数决定,即每个RegionMemStore的个数=表的列族的个数],可以通过配置来修改MemStore占用内存的大小,一般配置在128M-256M。
	Region 是分布式存储和集群负载均衡的最小单元,一个RegionServer 可以维护多个Region,但是同一个Region是不会被拆分成多个Region Server上。   
	Region 自动切分是Hbase能够拥有良好扩张的重要因素之一,也必然是所有分布式系统追求无限扩张的一副良药。
Hbase Region的拆分策略有比较多,最为常见的Hbase的切分策略有3种:ConstantSizeRegionSplitPolicyIncreasingToUpperBoundRegionSplitPolicySteppingSplitPolicy   

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iqHJSjCU-1637938991892)(C:\Users\86156\AppData\Local\YNote\data\weixinobU7Vjlm8ZJz94-Ski7f9_r4aMUU\39f1dd6a8bac44d3a99c276b00dcd0ad\clipboard.png)]

ConstantSizeRegionSplitPolicy
0.94版本之前默认的切分策略。这是最容易理解但也是最容易产生误解的切分策略。从字面的意思来看,当Region 大小大于某一阈值(hbase.hregion.max.filesize)之后就会触发切分。实际上并不是这样的,在真正的实现中这个阈值是对于某个Store来说的。即Region 中最大的Store大小大于设置的阈值之后才会触发切分。 这里的Store大小是指压缩后的文件大小。
ConstantSizeRegionSplitPolicy相对来说最容易想到,但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。
阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事。
阈值(hbase.hregion.max.filesize)设置较小对小表比较友好,但一个大表就会在整个集群产生大量的region,这对于集群的管理、资源使用、failover【故障转移】来说都不是一件好事。
IncreasingToUpperBoundRegionSplitPolicy
0.94版本~2.0版本默认切分策略。这种切分策略微微有些复杂,总体来看和ConstantSizeRegionSplitPolicy思路相同,一个region中最大store大小大于设置阈值就会触发切分。
但是这个阈值不像ConstantSizeRegionSplitPolicy 是一个固定值,而是会在一定的条件下不断地调整。调整规则和Region 所属的表在当前RegionServer上的Region个数有关系。
======================================================================================================= 
Math.min(tableRegionsCount^3 * initialSize,defaultRegionMaxFileSize)

	tableRegionCount:当前表在所在RegionServer上拥有的所有的Region数量的总和;
initialSize:如果定义了hbase.increasing.policy.initial.size,则使用该值,否则用memstore刷写值得2倍,即hbase.hregion.memstore.flush.size*2;
defaultRegionmaxFileSize:ConstantSizeRegionSplitPolicy所用到的配置项,也就是Region的最大大小;
Math.min:取这两个数值的最小值。

当初始hbase.hregion.memstore.flush.size定义为128M,过程为:
刚开始只有一个Region,上限为1^3 * 128 * 2=256M
当有2Region,上限为2^3 * 128 * 2=2048M
当有3Region,上限为3^3 * 128 * 2=6912M
以此类推当有4Region时候,为16G,上限达到了10GB,最大值就保持在了10G,Region数量再增加也不会增加上限
=======================================================================================================     
当然阈值并不会无限增大,最大值为用户设置的MaxRegionFileSize。这种切分策略很好的弥补了ConstantSizeRegionSplitPolicy 的短板,能够适应大表和小表。而且在大集群的条件下对于很多大表来说表现优秀,但是并不完美。
这种切分策略下很多小表在大集群中产生大量的小Region,分布在整个集群中。并且在Region发生迁移时可能触发Region分裂。     
SteppingSplitPolicy:
	2.0版本默认切分策略。这种切分策略的切分阈值又发生了变化,相比IncreasingToUpperBoundRegionSplitPolicy简单了一些。
依然和待分裂Region所属表当前RegionServer 上的Region个数有关系。
拆分规则为:
  If: region=1 
      then: flush size * 2 
  else: MaxRegionFileSize。 
还是以flushsize为128M、maxFileSize为10场景为列,计算出Region的分裂情况如下:
		第一次拆分大小为:2*128M=256M
  第二次拆分大小为:10G
从上面的计算我们可以看出,这种策略兼顾了ConstantSizeRegionSplitPolicy策略和IncreasingToUpperBoundRegionSplitPolicy策略,对于小表也肯呢个比较好的适配。          
Region切分准备工作-寻找SplitPoint
region切分策略会触发region切分,切分开始之后的第一件事是寻找切分点-splitpoint。
所有默认切分策略,无论是ConstantSizeRegionSplitPolicyIncreasingToUpperBoundRegionSplitPolicy抑或是SteppingSplitPolicy,对于切分点的定义都是一致的。
切分点是如何定位的呢?整个region中最大store中的最大文件中最中心的一个block的首个rowkey。另外,HBase还规定,如果定位到的rowkey是整个文件的首个rowkey或者最后一个rowkey的话,就认为没有切分点。
什么情况下会出现没有切分点的场景呢?最常见的就是一个文件只有一个block,执行split的时候就会发现无法切分。

很多新同学在测试split的时候往往都是新建一张新表,然后往新表中插入几条数据并执行一下flush,再执行split,奇迹般地发现数据表并没有真正执行切分。原因就在这里,这个时候仔细的话你翻看debug日志是可以看到这样的日志滴:

在这里插入图片描述

Region核心切分流程
HbaseRegion的整个切分过程包装成一个事务,意图能够保证事务切分的原子性。整个事务分裂过程分为3个阶段:Prepare-Execute-RollBack

在这里插入图片描述

Prepare阶段:在内存中初始化两个子Region,具体是生成两个HRegionInfo对象。包含tableName,RegionName,startKey,EndKey等。同时会生成一个Transaction Journal(事务日志),这个对象用来记录切分的进展,具体看RollBack阶段。
Execute阶段: Region 划分的核心操作

在这里插入图片描述

1RegionServer 首先将Zk节点下的/Region-in-Transaction中该Region的状态修改为Splitting
2Mater检测到Zk节点下的/Region-in-TransactionRegion 状态发生改变,修改内存中的Region状态
3、在HDFS父存储目录下新建临时文件夹.split,用于保存split后的子region信息
4、关闭父Region:Region关闭数据写入并触发flush操作,将写入到Region的数据全部持久化到磁盘。【此后短时间内客户端落在父region上的请求都会抛出NotServingRegionException。】
5、核心分裂步骤:在./split 文件夹下新建两个子文件夹,称为daughterA,daughterB。并在AB两个文件夹下生成reference 文件,分别指向父Region 中对应的文件。生成Refernece 文件的日志如下:

2017-08-12 11:53:38,158 DEBUG [StoreOpener-0155388346c3c919d3f05d7188e885e0-1] regionserver.StoreFileInfo: reference 'hdfs://hdfscluster/hbase-rsgroup/data/default/music/0155388346c3c919d3f05d7188e885e0/cf/d24415c4fb44427b8f698143e5c4d9dc.00bb6239169411e4d0ecb6ddfdbacf66' to region=00bb6239169411e4d0ecb6ddfdbacf66   hfile=d24415c4fb44427b8f698143e5c4d9dc。

其中reference 的文件名为: d24415c4fb44427b8f698143e5c4d9dc.00bb6239169411e4d0ecb6ddfdbacf66。前者为父rigion对应的HFile文件,后者为父Region。reference 文件的内容非常简单主要有两个部分组成:其一是切分点SplitKey,其二是Boolean 类型的变量(True 或者 false)。True表示该refernece文件引用的是父文件的上半部分,false 引用的是父文件的下部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6wGD6GZ-1637938991895)(C:\Users\86156\AppData\Local\YNote\data\weixinobU7Vjlm8ZJz94-Ski7f9_r4aMUU\1456c631e08345bdb2e9947a17d1860f\125036_16172.png)]

6、父Region 分裂成两个子Region后,将daughterA,daughterB拷贝到Hbase 根目录,形成两个新的Region7、parent region 通知修改Hbase mate表后下线,不在提供服务。下线后的Parent RegionMate 表中的信息并不会被立刻删除,而是标注Split列、offline列为True。并记录两个子region。   

在这里插入图片描述

8. 开启daughter A、daughter B两个子region。通知修改 hbase.meta 表,正式对外提供服务。

在这里插入图片描述

RollBack阶段:
	如果Execute 阶段出现异常,则执行RollBack。为了实现回滚,整个切分过程被分成很多子阶段。回滚程序会根据当前进程进展到哪个子阶段清理对应的垃圾数据。

在这里插入图片描述

九、LSM树详解
LSM 树(log-structured-Merge-Tree)并不是一棵严格意义上的树,它其实是一种存储结构。LSM树的核心特点就是利用顺序写来提高写性能,但是因为分层(此处的分层是指分为内存和文件两个部分)设计稍微会降低读的性能。

LSM 由以下三个重要组成部分:MemtableImmutable memtable、bLock cache  

img

MemTable
Memtable 是内存中的数据结构,用于存储最近更新的数据。并按照key有序的组织这些数据。LSM树对于具体如何有序地组织数据并没有明确的数据结构定义。例如Hbase使跳跃表来保证内存中key的有序。
因为数据暂时保存在内存中,内存并不是可靠存储,如果断电会丢失数据,因此通常会通过WAL【Write-ahead logging,预写式日志】的方式来保证数据的可靠性。
Immutable MemTableMemTable达到一定大小后,会转化成Immutable MemTableImmutable MemTable是将转MemTable变为SSTable的一种中间状态。写操作由新的MemTable处理,在转存过程中不阻塞数据更新操作。
SSTable(Sorted String Table)
	有序键值对集合,是LSM树组在磁盘中的数据结构。为了加快SSTable的读取,可以通过建立key的索引以及布隆过滤器来加快key的查找。
这里需要关注一个重点,LSM树 (Log-Structured-Merge-Tree)正如它的名字一样,LSM树会将所有的数据插入、修改、删除等操作记录(注意是操作记录)保存在内存之中,当此类操作达到一定的数据量后,再批量地顺序写入到磁盘当中。这与B+树不同,B+树数据的更新会直接在原数据所在处修改对应的值。LSM数的数据更新是日志式的,当一条数据更新是直接append一条更新记录完成的。
这样设计的目的就是为了顺序写,不断地将Immutable MemTable flush到持久化存储即可,而不用去修改之前的SSTable中的key,保证了顺序写。

当MemTable达到一定大小flush到持久化存储变成SSTable后,在不同的SSTable中,可能存在相同Key的记录,当然最新的那条记录才是准确的。这样设计的虽然大大提高了写性能,但同时也会带来一些问题:
1)冗余存储,对于某个key,实际上除了最新的那条记录外,其他的记录都是冗余无用的,但是仍然占用了存储空间。因此需要进行Compact操作(合并多个SSTable)来清除冗余的记录。
2)读取时需要从最新的倒着查询,直到找到某个key的记录。最坏情况需要查询完所有的SSTable,这里可以通过前面提到的索引/布隆过滤器来优化查找速度。

img

LSM Compact 策略
在Compact策略上,主要介绍两种基本的合并策略:size-tiered 和 leveled
	在介绍这两种合并策略之前,先介绍三种比较重要的概念,实际上不同的策略主要是围绕着这三种概念进行权衡和取舍
(1)读放大:读取数据时实际上读取的数据量大于真正的数据量。例如在LSM树中需要先在MemTable 查看当前key 是否存在,不存在则继续在SSTable中寻找。
(2)写放大:写入数据时实际上写入的数据量大于真正的数据量。例如在LSM树中写入时可能触发Compact操作,导致实际写入的数据量远远大于给key的数据量
(3)空间放大:数据实际占用的磁盘空间比数据真正大小更多。例如冗余存储,对于一个key来说,只有最新的那条记录时有效的,而之前的记录都是可以被清理回收的。
Size-tiered 策略
	Size-Tiered 策略保证每层的SSTable大小相近,同时限制每一层的SSTable的数量。例如每层限制SSTable的数量为N,当每层的SStable 达到N后,就会触发Compact操作合并这些SSTable。并将合并的结果写入到下一层的一个更大的SStable.
由此可以看出,当层数达到一定数量时,最底层的单个SSTable的大小会变得非常大。并且size-tiered策略会导致空间放大比较严重。
Leveled 策略
Leveled策略也是采用分层的思想。每一层的总大小固定,从上到下逐渐变大。
leveled会将每一层切分成多个大小相近的SSTable。这些SSTable是这一层全局有序的,意味着每一个key在每一层至多只有一条记录,不存在冗余记录。

假设存在以下这样的场景:
	1) L1的总大小超过L1本身大小限制:

img

	此时L1超过了最大阈值限制
2) 此时会从L1中选择至少一个文件,然后把它跟L2有交集的部分(非常关键)进行合并。生成的文件会放在L2:    

img

如上图所示,此时L1第二SSTable的key的范围覆盖了L2中前三个SSTable,那么就需要将L1中第二个SSTable与L2中前三个SSTable执行Compact操作。
3) 如果L2合并后的结果仍旧超出L2的阈值大小,需要重复之前的操作 —— 选至少一个文件然后把它合并到下一层:

img

需要注意的是,多个不相干的合并是可以并发进行的:
leveled策略相较于size-tiered策略来说,每层内key是不会重复的,即使是最坏的情况,除开最底层外,其余层都是重复key,按照相邻层大小比例为10来算,冗余占比也很小。因此空间放大问题得到缓解。
但是写放大问题会更加突出。举一个最坏场景,如果LevelN层某个SSTable的key的范围跨度非常大,覆盖了LevelN+1层所有key的范围,那么进行Compact时将涉及LevelN+1层的全部数据。
十、MemStore Flush
	HBase 是基于LSM-Tree模型,所有数据的更新操作都会首先写入到MemStore中(同时会顺序写入到Hlog 中)。当达到指定大小后再将这些修改操作批量写入到磁盘,生成一个HFile 文件。这种设计可以极大的提升Hbase的写性能;
	HBase 为了方便按照Rowkey进行检索,要求HFile中的数据按照RowKey进行排序。MemStore 数据在Flush前会对内存中的数据进行一次排序(快排),将数据有序化。
根据局部性原理,新写入的数据会有很大的概率被读取,因此Hbase在读取数据的时候会首先检查请求的数据是否在MemStore【写缓存】,MemStore 未找到的话再去,再去读缓存中查找,读缓存还没有找到,才回到Hfile 文件中查找,最终返回Merged的一个结果给用户。   
MemStoreHbase 的写入和 读取性能至关重要,Flush 操作又是MemStore中最核心的操作。
MemStore Flush触发条件
Hbase会在如下几种情况下触发Flush操作,需要注意的是MemStore的最小Flush单元是HRegion而非单个MemStore。可想而知,如果一个HRgionMemStore 过多,每次Flush的开销必然会很大。因此我们建议在进行表设计的时候尽量就按少CoulmnFamily的数量。
1MemStore级别的限制:当Region中任意一个MemStore的大小达到了上限 (hbase.hregion.memstore.flush.size,默认128MB),会触发Memstore刷新。
2Region级别的限制:当Region中所有MemStore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M),会触发memstore刷新。
3Region Server级别的限制:当一个RegionServer 中所有的MemStore 大小总和达到了上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),才会触发MemStore 的刷新操作。Flush 顺序是按照MemStore 由大到小执行,先执行MemStore 最大的Region,再执行大的。直到总体的MemStore的使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)。
4WAl 级别的限制:当一个Region serVer 中Hlog的数量达到上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个HLog对应的一个或者多个Region进行Flush
5Hbase 定期刷新MemStore:默认周期为1小时,确保MemStore不会长时间没有持久化。为了避免所有的MemStore 在同一时间都将进行Flush操作,定期的Flush操作会有20000ms左右的随即延迟。
6、手动执行Flush:用户可以通过shell 命令 flush 'Table Name' 或者 Flush 'Region name' 分别对一个表或者一个Region 进行flush
Memstore Flush流程
为了减少Flush 过程对于读写的影响,Hbase采用类似于两阶段提交的方式,将整个Flush过程分为三个阶段:
1、prepare阶段:遍历当前Region中所有的MemStore,将MemStore中当前数据集kvset做一个快照snapshot。然后再先建一个新的KVSet。后期所有的写入操作都会写入到的新的kvset。
整个Flush阶段的读取操作首先会遍历kvset和snapshot,如果找不到再会在HFile 中查找。
Prepare 阶段需要加一把updateLock 对写请求进行阻塞,结束之后释放该锁。因为此阶段没有任何的费时操作,因此持锁时间很短。

2Flush 阶段:遍历所有的MemStore,将prepare 阶段生成的snapshot 持久化为临时文件,临时文件是存放在对应 Region 文件夹下面的 .tmp 目录里面。因为这个阶段涉及磁盘IO,因此相对比较耗时。

3、commit 阶段:遍历所有的MemStorePrepare 阶段生成的临时文件转移到指定的ColumnFamily 目录下,最后清空prepare 阶段生成的snapshot。
十一、StoreFile (HFile) Compaction
	Hbase 是一种Log-Structured-Merge-Tree 架构模式,用户数据写入先写入到Wal,再写入到写缓存。当满足一定条件后缓存数据会执行Flush操作真正的落盘,生成一个Hfile 文件。随着数据写入的增多,Flush 的次数也会不断增多,进而HFile 数据文件就会越来越多。然而,太多的数据文件会导致数据查询的IO次数增加。Hbase尝试着不断将这些文件进行合并,这个合并称为Compaction

Compaction 会从一个Region的一个Store中选择一些HFile 文件进行合并。需要说明的是,compaction都是以Store为单位进行的
合并原理如下:先从这些待合并的数据文件中读出KeyValues,再按照由小到大排序后写入到一个新的文件中。之后这个新生成的文件将取代之前待合并的所有文件对外提供服务。    
Hbase 根据合并规模将Compaction分为两类:MinorCompationMajorCompation
MinorCompaction:是指选取一些小的,相邻的StoreFile将它们合并成一个更大的StoreFile。这个过程不会处理已经被delete或者expired 的cell。一次MinorCompaction 的结果是更少并且更大的StoreFile
MajorCompaction:是指将 Region 下所有的StoreFile合并成一个StoreFile。这个过程会处理三类没有意义的数据:同时删除过期数据、已删除数据(打了Delete标记的)、版本过大的数据等三类无效数据。一般情况下,Major Compaction持续时间比较长,整个过程会耗费大量的系统资源,对上层业务有较大的影响。因此线上业务都会关闭自动触发MajorCompaction 功能,改为手动在业务低峰期触发。   

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6X7eso5-1637938991898)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210215114006632.png)]

Compaction 触发时机:
Hbase中触发Comapction的因素有很多,最常见的因素有三种:MemStore Flush,后台线程周期性检查,手动触发。
1MemStore Flush: 应该说FlushCompaction的源头,MemStore Flush 会产生HFile文件,文件越多就越需要CompactHbase 每次Flush之后都会对当前Store中的文件数进行判断,一旦文件数大于配置,就会触发compaction。需要说明的是,compaction都是以Store为单位进行的,而在Flush触发条件下,整个Region的所有Store都会执行compact,所以会在短时间内执行多次compaction。
2、后台线程周期性的检查:后台线程CompactionChecker 周期性检查是否需要执行Compaction,检查周期为hbase.server.thread.wakefrequency * hbase.server.compactchecker.interval.multiplier
hbase.server.thread.wakefrequency:是HBase服务端线程唤醒时间间隔
hbase.server.compactchecker.interval.multiplier:是compaction操作周期性检查乘数因子
	该线程优先检查文件数是否大于配置,一旦大于就会触发ComPaction。如果不满足,他就会检查是否满足MajorComPaction 条件。简单来说,如果当前StoreHfile 最早的更新时间早于某个值MCTime,就会触发Major CompactionMCTime是一个浮动值,浮动区间为[7-7*0.2,7+7*0.2]7Hbase.Hregion.majorcompaction,0.2Hbase.Hregion.majorcompaction.jitter。可见默认为7天左右执行一次major compaction。如果想要禁用major compaction,只需要将Hbase.Hregion.majorcompaction设置为0
3、手动触发:一般来讲,手动触发compaction通常是为了执行major compaction。
  原因有三,其一是因为很多业务担心自动major compaction影响读写性能,因此会选择低峰期手动触发;
  其二也有可能是用户在执行完alter操作之后希望立刻生效,执行手动触发major compaction;
  其三是HBase管理员发现硬盘容量不够的情况下手动触发major compaction删除大量过期数据;
  无论哪种触发动机,一旦手动触发,HBase会不做很多自动化检查,直接执行合并。
Compaction过程会有以下作用:
	1、合并文件
	2、清除删除、过期、多余版本的数据
	3、提高读写数据的效率
十二、HFile
	Hbase的rowkey+column family+column qualifier+timestamp+value是Hfile中数据的排列依据。
HFileHbase存储数据的文件组织形式。对数据的检索为dataBlock 级别的,而不是行级别的。所以这种KeyHFile 内部粗粒度的本地索引主键。
从HBase开始到现在,HFile经历了三个版本。其中V2是0.92引入,V3是0.98引入。在HFile实际使用过程中发现V1占用内存比较多,V2对此进行了优化,V3版本基本上和V2相同,只是在Cell层面添加了Tag数组的支持。    
HFile v1

HFile v1的逻辑数据结构如下图:DataBlock区域、mateBlock【Bloom Filter】、FileinfoDataBlockIndexMateBlockIndexTrailer 六部分组成。
HFile V1 在实际使用过程中发现他占用的内存比较多,并且Bloom FilterBlock Index会变得很大。
Bloom Filter 可以增长到100MB,这会在查询时引起性能的问题,因为加载并查询Bloom FilterBlockIndex 在一个RegionServer 中可能会达到的6GB, RegionServer 在启动时需要加载这些BlockIndex,因而增加了启动时间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j28cfkBe-1637938991898)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210215170136352.png)]

HFile v2
	HFile V2 的逻辑结构如下图所示:

在这里插入图片描述

文件主要分为四部分:Scanned Block section、Non-Scanned Block section、Load-on-open SectionTrailer

1Scanned Block Section:表示顺序扫描Hfile时所有的数据块都将被读取(dataBlock/Leaf index block/Bloom Block2Non-Scanner Block Section: 表示顺序扫描Hfile时数据不会被读取,主要包括(Leaf index BlockIntermediate Level 	data index Blocks)两部分。 

3Load-on-open section: 这部分数据在HbaseRegionServer启动时,需要加载到内存中。包括(FileInfoBloom filter 		Block、data Block index和meta Block index)

4Trailer:这部分数据记录了Hfile的基本信息、各个部分的偏移值和寻址信息。

HFile 在读取数据的时候首先会解析Trailer Block 并加载到内存中,然后再加载Load-On-Open section的数据   
HFile V2 的物理结构如下图所示

在这里插入图片描述

	Hfile 被分割成大小相等的Block,每个Block的大小可以在创建表列族的时候通过参数BlockSize =>65535’ 进行指定默认为64K。大号的Block 有利于Scan,小号的Block有利于随机查询。
	并且所有的Block都拥有相同的数据结构,HbaseBlock 抽象为统一的HFileBlockHfileBlock支持两种类型:一种类型支持checkSum,一种支持。下图选用不支持CheckSumHFileBlock的内部结构。       

在这里插入图片描述

	HFileBlock 主要包括两部分:BlockheaderBlockData。其中BlockHeader 主要用于存储Block元数据,BlockData用来存储具体数据。Block元数据中最为重要的是BlockType字段,用来标注该Block块的类型。
Hbase中定义了8BlockType,每种BlockType对应的Block都存储不同的数据类型,有的存储用户数据,有的存储索引数据,有的存储meta元数据。对于任意一种HFileBlock,都拥有相同结构的BlockHeader,但是BlockData结构却不同。

在这里插入图片描述

HFile层面上将文件切分成多种类型的Block,下面重点介绍记录Hfile基本信息的Trailer Block、存储用户实际数据的DataBlock、布隆过滤器相关的块。

(1) Trailer Block主要记录了HFile的基本信息、各部分的偏移值和寻址信息,HFile 在读取数据的时候首先解析Trailer Block并加载到内存中,然后进一步加载Load-on-open section的数据。 

具体步骤为:
1、首先加载Version 版本信息,Hbase version 主要包含MajorVersionMinor Version 两部分。前者取决于HFile的主版本V1,V2,V3;后者在主版本的基础上决定是否支持一些微小的调整,比如是否支持checkSum。不同的版本决定了使用不同的Reader对象对Hfile 进行解析
2、根据Version 信息获取Trailer 长度(不同的version Trailer 长度不同),再根据Trailer 长度加载整个HFileTRailer Block
3、最后加载Load-on-Open 部分到内存。 

在这里插入图片描述

	(2) Data Block
	DataBlockHbase 中数据存储的最小单元。DataBlock中主要存储用户的KeyValue数据(KeyValue 后面一般会跟着一个timeStamp,图中未标出),而KeyVlaue结构是Hbase存储的核心,每个数据都是以KeyValue结构在Hbase 中进行存储。
每个KeyValue 都是由4部分构成,分别是Key length,Value Length,key 和 value。
其中key length 和 value length 是两个固定长度的数值。
而key是一个复杂的结构,首先是rowkey的长度,接着是rowkey;然后是ColumnFamily 的长度,再是ColumnFamily;之后是ColumnQualifier 长度和ColumnQualifier 值。最后是时间戳和KeyType [keyType 有4中类型,分别是put,Delete,DeleteColumnDeleteFamily]
value没有复杂的结构,就是一串纯粹的二进制数据。

在这里插入图片描述

	(3)布隆过滤器相关的块 BloomFilter Meta Block & Bloom Block
BloomFilter 对于Hbase 的随机读性能至关重要,对于get操作以及部分的Scan 操作可以剔除掉不会用得到的Hfile 文件,减少实际的IO次数,提高随机读性能。   
BloomFilter 的工作原理
	BloomFilter 使用位数组来实现过滤,初始状态下位数组的每一位都为0,如下图所示       

>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OZYPSoEm-1637938991902)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210216104344357.png)]

	假设此时有一个集合S={x1,x2...xn},Bloom Filter 会使用K 个独立的hash 函数,分别将集合中的每一个元素映射到{1,...m}的范围。对于任何一个元素,被映射到的数字作为作为对应的位数组的索引,该位会被置为1.
下图中集合S只有两个元素x和y,分别被3个hash函数进行映射,映射到的位置分别为(036)和(4710),对应的位会被置为1:

在这里插入图片描述

	现在假如要判断另一个元素是否在此S集合中,只需要将该元素通过这3Hash函数进行映射,并查看对应的位置上是否有0存在。如果有的话,则表示此元素肯定不存在于该集合中,否则有可能存在。下图所示 Z肯定不在集合{x,y}

>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ucxX3NHT-1637938991903)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210216105426929.png)]

Hbase 中的每一个HFile 都有对应的位数组,KeyValue在写入到Hfile 会先经过几个Hash函数的映射,映射后经对应的数组位改为1。在执行Get 请求之后再进行Hash 映射,如果对应的数组位为0,则说明Get 请求查询的数据不在给HFile
	Bloom Block 用于存储HFile中位数组的值。可以想象,一个Hfile 文件越大,里面存储的KeyValue值越多,位数组就会相应越大。一旦太大就不适合加载到内存中。因此HFileV2 在设计上将位数组进行了拆分,拆成多个独立的位数组(根据Key进行拆分,一部分连续的Key使用一个位数组)。这样一个Hfile 中就会包含多个位数组,只需要根据Key 进行查询,首先定位到具体的某个位数组,只需要将此位数组加载到内存中进行过滤即可,减少了内存开支。
在结构上每一个位数组对应HFile 中的一个Bloom Block,为了方便根据key 定位具体加载哪一个位数组,HFile V2 设计了对应的索引 Bloom Index BlockBloom Index Block对应的内存和逻辑结构如下:   

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pRnege7S-1637938991903)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210216112452354.png)]

	Bloom Index Block结构中totalByteSize表示位数组的bit数;
numChunks表示Bloom Block的个数;
	hashCount表示hash函数的个数;
	hashType表示hash函数的类型;
	totalKeyCount表示bloom filter当前已经包含的key的数目;
	totalMaxKeys表示bloom filter当前最多包含的key的数目;
	Bloom Index Entry对应每一个bloom filter block的索引条目,作为索引分别指向’scanned block section’部分的Bloom BlockBloom Block中就存储了对应的位数组。


Bloom Index Entry 的结构见上图左边所示,BlockOffset 表示对应的Bloom BlockHFile 中的偏移量,FirstKey 表示对应的BLoomBlock 的第一个Key。

根据上文所述,一次get请求进来,首先会根据key在所有的索引条目中进行二分查找,找到对应的Bloom Index entry,就可以定位该key对应的位数组,记载到内存就可以进行过滤判断。   
十三、HFile索引机制
	Hbase 中索引结构根据索引层次不同分为:Single Level 和 mutil-Level。前者是单层索引,后者是多层索引一般为两级或者三级。
	HFile V1 中只有single level 一种索引结构,V2 版本中引入多级索引结构。之所以你引入多级索引结构的原因在于随着Hfile文件越来越大,DataBlock 越来越多,索引数据也越来越多,无法直接加载到内存中,多级索引可以只加载部分索引,降低内存的使用。
Bloom Filter内存使用问题是促使V1版本升级到V2版本的一个原因,再加上这个原因,这两个原因就是V1版本升级到V2版本最重要的两个因素。    
V2 版本中的索引块分为两大类:Root Index BlockNonRoot Index Block,其中NonRoot Index block 又分为 Intermediate Index Block 和 leaf Index Block两种。HFile中索引结构类似于一棵树,Root Index Block表示索引数根节点,Intermediate Index Block表示中间节点,Leaf Index block表示叶子节点,叶子节点直接指向实际数据块。


Bloom Block也需要索引,索引结构实际上就是采用了single-level结构,文中Bloom Index Block就是一种Root Index Block。   

对于DataBlock,由于Hfile 刚开始数据量较小,索引采用single-level 结构,只有Root Index一层索引,直接指向数据块。当数据量慢慢变大,Root Index Block 满了之后,索引就会变成 mutil-level 结构,有一层索引变成两层,根节点指向叶子节点,叶子节点指向实际的数据块。如果数据量再大索引层次就会变成3层。    
Root Index Block 
	表示索引树根节点索引块,可以作为bloom的直接索引,也可以作为data索引的根索引。而且对于single-level和mutil-level两种索引结构对应的Root Index Block略有不同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QRcGtii2-1637938991904)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210216122021050.png)]

Index Entry表示具体的索引对象,每个索引对象由3个字段组成;Block Offset表示索引指向数据块的偏移量;BlockDataSize表示索引指向数据块在磁盘上的大小;BlockKey表示索引指向数据块中的第一个key。

除此之外,还有另外3个字段用来记录MidKey的相关信息,MidKey表示HFile所有Data Block中中间的一个Data Block,用于在对HFile进行split操作时,快速定位HFile的中间位置。需要注意的是**single-level索引结构和mutil-level结构相比,就只缺少MidKey这三个字段**
NonRoot Index BlockRoot Index Block相同,NonRoot Index Block中最核心的字段也是Index Entry,用于指向叶子节点块或者数据块。不同的是,NonRoot Index Block结构中增加了block块的内部索引entry Offset字段,entry Offset表示index Entry在该block中的相对偏移量(相对于第一个index Entry),用于实现block内的二分查找。
mutil-level结构中NonRoot Index Block作为中间层节点或者叶子节点存在,无论是中间节点还是叶子节点,其都拥有相同的结构,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6RC1YwJI-1637938991905)(C:\Users\86156\AppData\Roaming\Typora\typora-user-images\image-20210216122637201.png)]

索引流程
	图中上面三层为索引层,在数据量不大的时候只有最上面一层,数据量大了之后开始分裂为多层,最多三层,如图所示。最下面一层为数据层,存储用户的实际keyvalue数据。
	基本流程可以表示为:
	1. 用户输入rowkey为fb,在root index block中通过二分查找定位到fb在’a’和’m’之间,因此需要访问索引’a’指向的中间节点。因为root index block常驻内存,所以这个过程很快。
	2. 将索引’a’指向的中间节点索引块加载到内存,然后通过二分查找定位到fb在index ‘d’和’h’之间,接下来访问索引’d’指向的叶子节点。
	3. 同理,将索引’d’指向的中间节点索引块加载到内存,一样通过二分查找定位找到fb在index ‘f’和’g’之间,最后需要访问索引’f’指向的数据块节点。
	4. 将索引’f’指向的数据块加载到内存,通过遍历的方式找到对应的keyvalue。

	上述流程中因为中间节点、叶子节点和数据块都需要加载到内存,所以io次数正常为3次。但是实际上HBase为block提供了缓存机制,可以将频繁使用的block缓存在内存中,可以进一步加快实际读取过程。所以,在HBase中,通常一次随机读请求最多会产生3次io,如果数据量小(只有一层索引),数据已经缓存到了内存,就不会产生io。
十四、HBase行级事务模型
事务必须满足ACID属性:
	原子性:事务中一系列操作要么全部完成,要么全部不完成。
隔离性:如果有两个事务,运行在相同的时间内,执行相同的功能。事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。
	一致性:事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
持久性:在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
目前Hbase只支持行级事务
	(1)Hbase 事务原子性保证
Hbase数据首先写入到WAL,再写入MemStore。写入MemStore异常很容易造成回滚。因此保证写入/更新的原子性只需要保证写入到WAL的原子性即可。
在Hbase 0.98 之前版本保证Wal写入的原子性并不是很容易,这是由WAl的结构决定的。假设一个行级事务更新R行中的3列(c1、c2、c3)

之前版本的WAL: 
<logseq1-for-edit1>:<KeyValue-for-edit-c1> 
<logseq2-for-edit2>:<KeyValue-for-edit-c2> 
<logseq3-for-edit3>:<KeyValue-for-edit-c3> 
每个KV 都会生成一个WAL单元,这样一个行级事务更新了多少列就会生生成多少个WAL单元。在将这些WAL单元append到日志文件的时候,一旦出现宕机或者其他异常,就会出现部分写入成功部分写入失败,原子性得不到保障。

当前版本的WAL:
	<logseq#-for-entire-txn>:<WALEdit-for-entire-txn> 
<logseq#-for-entire-txn>:<-1, 3, <Keyvalue-for-edit-c1>, <KeyValue-for- edit-c2>, <KeyValue-for-edit-c3>>
通过这种结构,每个事务只会产生一个WAL单元。这样就可以保证WAL写入时候的原子性。    
	(2)Hbase 事务隔离性
	2.1如何实现写写并发控制?
  实现写写并发其实很简单,只需要在写入(或更新)之前先获取行锁,如果获取不到行锁,则表明已经有其他线程拿了该锁。就需要不断地重试等待,直到其他的线程释放行锁。拿到行锁之后开始写入数据,写入数据之后释放行锁即可。
  2.2 如何实现批量写入多行的写写并发?两阶段锁协议
  Hbase 支持批量写入,即一个线程同时更新一个Region中的多行记录。那如何保证当前事务中的批量写入和其他事务中的批量写入的并发控制那?
  首先获取所有待写入行记录的行锁;开始执行写入操作;写入完成之后在统一释放所有行记录的行锁
  不能更新一行锁定/释放一行,这样容易造成多个事务之间的死锁。
十五、META表
META表主要用于存储Region的分布情况以及每个Region的详细信息,META表的结构如下图:
META 表的每条ROW记录了Region信息。

img

	ROWKey 主要由三部分构成:TableName,StartKey,TimeStamp,Row存储的内容我们又称为RegionName。

然后是表中主要列族:Info, Info主要包含3Column:regionInfo,Server,ServerStartCodeRegionInfo就是Region的详细信息,包括StartKey,EndKey以及每个Family的信息等等。Server 存储的是管理这个RegionRegionServer地址。

当Region 被拆分、合并、重新分配时,都需要修改这张表的内容。    
假设Hbase中只有两张用户表,Table1Table2Table1 非常大,被划分成很多个Region。因此在Meta表中有很多条Row来记录这些Region。而Table2 很小,只是被划分成两个Region,因此在META 表中只有两条Row 来记录

在这里插入图片描述

假设我们要在Table2 中查询一条RowKey 为RK10000的记录。那么我们需要遵循一下步骤
	1.从meta表里面查询哪个Region 包括这条数据。
	2.获取管理这个RegionRegionServer 地址
	3.连接这个RegionServer,查到这条数据
为了知道哪个RegionServer 管理了Meta 表,我们把管理Meta表的RegionServer 地址放到ZK 上面即可。 

0.96 版本之前考虑meta表过大,可能需要将meta 划分成多个Region.这就意味着可能需要多个RegionServer管理Meta表,因此需要两一个表来记录MetaRegion 的详细信息,即引入Root 表,Root 表的结构和Meta 表结构基本一致。最后由ZK 存储Root 表的位置信息。
所有客户端访问用户数据前,首先需要访问ZK获取root 表的位置,然后访问Root表获取Meta 表的位置。最后通过Meta表的信息确定用户数据的存放位置。

img

0.98版本后,hbase:mata表不在split,只有一个region,也去除掉了hbase:root表,client的访问过程也不用进行这一步。
舍弃Root 表的原因在于:
  一条HBase 的元数据信息大于在100字节
  Block 的默认大小为128M=134217728B
  一个BLock 大约存储134W 条元数据
  一个表最多也就3-5元数据。
  也就是一个BLock 能存储26W 个表。
  一个项目再复杂,表的个数一般不会过百,所以Root 表存在没有意义
十六、Hbase 故障恢复之RegionServer 宕机恢复
	HBase 故障恢复我们以RegionServer 宕机恢复为例,引起RegionServer宕机的原因各种各样,例如:GC导致、网络异常、DataNode 异常。
上述这些场景中一旦RegionServer发生故障,Hbase都会马上检测到这种宕机。并且在检测到宕机之后将宕机的RegionServer上的所有Region 重新分配到其他正常的RegionServer 上去。再根据HLog 进行数据丢失恢复,恢复完成之后即可对外提供服务。整个过程完全自动完成,无需人工介入。     

h3

	Hbase检测宕机是通过ZooKeeper实现的,正常情况下RegionServer 会周期性向ZooKeeper 发送心跳,一旦发生宕机,心跳就会停止,超过一定的时间ZooKeeper 就会认为RegionServer 发生了宕机,并将消息通知发送给Master。

根据HLog数据丢失恢复,需要对HLog进行划分。在0.98版本中一台RegionServer 只有一个Hlog,即该台Regionserver 的所有Region 日志都混合写入到HLog 中。然而日志回放是以Region为单位的,一个Region一个Region的回放,因此在回放前需要对HLog按照Region 进行分组。将每个Region的日志数据都放到一起,方便后面按照Region 进行数据的回放。这个按照Region对日志进行分组称之为HLog 切分。    
	根据实现方式不同,Hbase 的故障恢复先后经历了三种不同的模式。

h4

1Log Splitting :整个过程都是由Master控制,效率低下
	a.将待切分的日志文件夹重命名,防止RegionServer 未发生真的宕机而持续的向Hlog 写入日志。
	b.Master 启动一个进程依次读取出每个Hlog 中所有的<HLogKey,WALEdit>,根据HLogKey 所属的Region 不同写入到不同的内存Buffer 中。这样整个Hlog 所有数据会被完整Group 到不同的BUffer 中。
	c.每个Buffer会对应启动一个写线程,负责将Buffer 中的数据写入到HDFS 中,等到Region 重新分配到其他RegionServer后按顺序回放对应的REgion 的日志数据。

h5

2Distributed Log Spliting
Distributed Log SplitingLog Splitting 的分布式实现,它借助Master 和所有的region Server的计算能力进行Hlog的日志切分。其中Master 作为协作者,RegionServer 作为主要实际工作者。
a.Master会将带切分的日志路径发布到ZooKeeper节点(/Hbase/splitWal),每一个日志作为一个任务,都有对应的任务状态Task_UnAssginedb.RegionServer 启动后都会注册在这个ZK节点等待任务,一旦Master发布任务之后,RegionServer 就会抢占该任务
c.抢占任务实际上首先查看任务的状态,如果zk节点任务是Task_UnAssigned 状态,表明当前任务没有人占用,此时就会修改zk节点任务状态为Task_Owned。如果修改失败,则表明其他的RegionServer 也在抢占,修改成功表明任务抢占成功。
d.RegionServer取得任务后会让对应的HLogSplitter线程处理Hlog的切分,切分的时候读取出Hlog的对,然后写入不同的Region buffer的内存中;
	RegionServer启动对应写线程,将Region buffer的数据写入到HDFS中,路径为/hbase/table/region/seqenceid.temp,seqenceid是一个日志中该Region对应的最大sequenceid,如果日志切分成功,而RegionServer会将对应的ZK节点的任务修改为TASK_DONE,如果切分失败,则会将任务修改为TASK_ERR。

e.Master一直会监听ZK节点,一旦发生任务状态修改就会得到通知。如果任务状态修改为Task_Err,Master 就会重新发布任务。如果任务状态修改为Task_DONe,Master 就会将对应的节点删除。 

f、Master重新将宕机的RegionServer中的Rgion分配到正常的RegionServer中,对应的RegionServer读取Region的数据,将该region目录下的一系列的seqenceid.temp进行从小到大进行重放,从而实现对应Region数据的恢复。

这种Distributed Log Splitting 方式在很大程度上加快了故障恢复的进程,正常的故障恢复可以降低到分钟级别。然而这种方式会产生大量的小文件,产生的小文件数量为M * N。其中M 为待切分的HLog 数量, N 是一个宕机的Region Server 上的Region 数量。

h6

3Distributed Log RePlay
	Distributed Log RePlayDisTributed Log Splitting 不同的是先将宕机的RegionServer 上的Region 分配给正常的RegionServer,并将该Region 标记为ReCovering。
在使用DisTributed Log Splitting的方式进行HLog的划分,不同的是,RegionServerHlog切分到对应的Buffer之后,并不写入到文件中。而是直接进行重放。这样可以大大减少了文件数量,减少了IO消耗。

在这里插入图片描述

HBase 面试知识点
一、简单介绍一下HBase
HBase概述
Hbase 是建立在HDFS之上的分布式NoSQL数据库;
	适合对于海量数据进行实时的随机读写;
	一张HBase表能够支撑数亿行,数百万列;

数据模型
	NameSpace【命名空间】:类似于关系型数据库得database 概念,每一个命名空间下可以有多个表。Hbase 自带了两个命名空间:hbase 和 default。hbase 主要用于存储hbase的内置表;default是用户默认使用命名空间。
	table:类似于关系型数据库表的概念。与关系型数据库表不同的是,Hbase 定义表时只需要声明列族即可,无需声明具体的列。因此往Hbase 写入数据时,字段可以动态、按需指定。与关系型数据库相比,hbase 能够轻松适应字段变更的情景。
	Row: Hbase 表的每行数据都由一个RowKey 和 多个ColumnFamily 构成,并且数据的存储是按照RowKey顺序进行存储的,查询时也是根据RowKey进行检索,所以Rowkey的设计非常重要。
	Column: HBase 表的每列都是由一个ColumnFamily 和 多个Column Qualifier (列限定符),建表时,只需要指定列祖,列限定符无需指定。
	TimeStamp:用于标识数据的不同版本,每条数据写入时,系统都会自动为其加上该字段,值为写入Hbase的时间。
	Cell:{RowKey,ColumnFamily,ColumnQualifier,timeStamp} 唯一确定的单元。Cell数据没有类型,全部以字节码的形式存储

基础架构
	(1HMaster:相当于HBase的大脑,充当Region Server 的管理者。
当HRegionSrever中存储的数据表过大以后,HMaster通知HRegionSrever对表进行切割,实现集群的负载均衡;HRegionSrever故障失效时,HMaster负责此节点上所有数据的迁移。
同时为RegionServer 分配Region。

同时整个HBase的数据读写操作都是通过HMaster进行管理和通知的;

	(2RegionServer:Hbase 的核心组件,负责执行Hbase 所有数据的读写操作。由WAL、RegionBlockCache等多个组件构成

		Region:RegionServer中数据存储的组件,Table在行的方向上分隔成多个Region,RegionHbase中分布式存储和负载均衡的最小单位。他们可以分布在多个或者一个RegionServer上,Region由一个或者多个Store组成[每一个ColumnFamily 建一个Store]Store:其内部包含MemStoreStoreFile两个组件,前者以内存的形式存储数据,后者以文件的形式存储数据。
		MemStore:俗称写缓存,存放在内存中,用于保存修改的数据。当MemStore的大小达到一定阈值默认为64M,就会被FlushHFile中。由于HFile 要求数据是有序的,所以需要在MemStore中对Key-value数据进行排序,等待刷写时机进行刷写到Hfile。每次刷写都会生成一个Hfile 文件。这样能充分利用hadoop写入大文件的性能优势,提高写入性能。

		StoreFile:MemStore中的数据写入到文件后就是StoreFileStoreFile 的底层以Hfile的形式进行存储。

		WAL:Write Ahead Log 预写日志。由于数据先存放在MemStore 中,这有很高的概率导致数据丢失。为了解决这一问题,数据先写入到WAL文件中,再写入到MemStore。当系统出现故障时,可利用日志文件进行重建。

		BlockCache: 俗称读缓存,每次查询的数据都会被缓存到BlockCache 中,方便下一次查询。

	(3)ZK:作为HBase Master的HA解决方案,即保证集群中只有一个Master处于工作状态。同时还监控RegionServer 的工作状态,当RegionServer 出现异常时通知Master 进行处理。
二、为什么要使用HBase
1)大:Hbase可存储大量的数据,Hbase可存储PB级数据,支持数亿行,上百万列;
(2)面向列存储:Hbase的表由列族构成,以列为存储单位。
(3)无模式:每一行由一个可排序的RowKey和任意多列构成,列可以按需求动态增加。 
(4)稀疏:空列并不占用资源,表可以设计的非常稀疏。
(5)数据类型单一:Hbase 中的数据都是字符串,没有类型。
(6)数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳;    
三、HBase 适用于什么场景
1、半结构化或者非结构化数据:对于数据结构不确定或者杂乱无章很难按照统一的概念进行抽取数据就可以使用HBase

2、记录非常稀疏:关系型数据库表中的行有多少列都是固定的,为Null的列浪费了存储空间。但是HbaseNull 的列不会被存储,这样既可以节省空间又可以提升读性能。

3、多版本数据:根据RoWkeyColumnFamily 以及ColumnQualifications定位到的Value可以有任意数量的版本值。因此对于需要存储变动历史记录的数据,Hbase 就非常方便。

4、超大量数据存储:Hbase可存储大量的数据,Hbase可存储PB级数据,支持数亿行,上百万列;   
四、HBase和关系型数据库的区别
1、关系型数据库采用关系模型,数据以表的形式存在,数据类型和存储方式多样化,且操作复杂。

HBase中的数据以Region形式存在,每个Region中包含多个列族,将数据存储为未经解释的字符串,没有复杂的表间关系。只有简单的添加查询等,不支持join操作

2、存储模式:Hbase基于列存储,每个列族有几个文件构成,不同列族分开保存。

3、数据索引:关系型数据库针对不同的列构建多个索引,HBase只有一个索引----行键,所有访问都通过行键进行访问或扫描。
五、HBase和Hive的区别?

img

六、HBase 表的RowKey 设计原理
1RowKey 长度原则:RowKey是二进制流,开发者建议长度为10-100字节,不过建议越短越好,最好不超过16字节。

原因在于:
	数据的持久化文件HFile是按照KeyValue存储的,如果RowKey太长的话就会影响HFile的存储效率。
	  MemStore将缓存数据到内存,如果RowKey字段过长,则造成内存的有效利用率降低,系统将无法缓存更多地数据,降低了检索的效率。
	  目前的操作系统都是64位系统,内存8字节对齐。控制在16个字节,8字节的整数倍利用了系统的最佳特性。

2RowKey散列原则
如果RowKey按照时间戳递增,不要将时间放在二进制码前面。建议将RowKey的高位作为散列字段,由程序循环生成;低位放时间字段。这样将提高数据均衡分布每个Regionserver,实现RegionServer负载均衡的几率。

如果没有散列字段,首字段直接是时间就会产生所有的新数据都在一个RegionServer上堆积的热点问题。这样在查询时负载将集中在个别RegionServer上,降低了查询效率。

3、唯一性原则
必须在设计上保证RowKey的唯一性。
七、如何避免HBase 行键热点问题
	HBase热点现象:检索Hbase记录要通过RowKey来定位数据行,当大量的客户端请求集中访问集群中的一个或少数据几个节点时,就会造成这些节点的负载较大。最终导致单个主机的负载过大,引起性能的下降甚至是Region 不可用。

热点产生的原因:有大量连续编号的RowKey集中在个别Region中

避免方法:
1、在RowKey前面加上随机数,此方法适用于Hbase作为海量数据存储但是不频繁查询的业务场景。
2Hash散列,将数据打乱
3、将RowKey字段和时间戳反转。
八、Hbase 的查询方式及异同
1、全表查询: scan tableName
2、基于rowkey的单行查询: get tableName,'1'
3、基于RowKey的范围查询:scan tableName,{STARTROW=>'1',STOPROW<='2'} 
GetScan 方法
	1、按指定的RowKey获取唯一的一条数据,使用get方法。get方法分为两种:分别是设置了closestRowBefore 和 没有设置 ClosestRowBefore 的rowlock。主要是用来保证行的事务性。即每一个get是以一个Row来标记的,一个Row可以有多个FamilyColumn2、按指定条件获取一批记录,scan实现按条件查询。
	scan 可以通过SetCachingSetBatch 方法提高速度(空间换时间)
	scan 可以通过setStartRow 和 setEndRow 来限定范围,startRow是闭区间,endRow是开区间,范围越小,性能越高。
	scan 可以通过setFilter方法添加过滤器,这也是分页、多条件查询的基础。
九、请描述HBase 中Scan 对象的SetChache 和 setBatch的方法使用
	SetCache 用于设置缓存,即设置一次RPC请求可以返回多少行数据。对于缓存操作来说,如果返回的行数太多,可能会造成内存溢出。这个时候就需要使用SetBatchSetBatch 用于设置批处理,设置这个之后客户端可以选择返回的列数。如果一行中包括的列数超过了设置值,那么就会将该列分片。例如,如果一行中有17列,batch 设置为5的话,返回4组分别四5552Cache设置了服务器一次返回的行数,Batch 设置了服务器一次返回的列数,只针对一行数据而言。    
  RPC请求次数=(行数*每行列数)/MIN(每行列数,批大小)/扫描器缓存
十、Hbase 的Cell 结构
	Hbase 通过RowColumn 确定一个存储单元,称为Cell
Cell{RowKey,ColumnFamily,ColumnQualifier,Version}构成,cell 中的数据是没有类型的,全部是字节码形式存贮。
十一、Hbase 的Compact 机制
Hbase 中每当MemStore的数据被Flush 到磁盘之后,就会生成一个StoreFile文件。当StoreFile 文件数量达到一定程度之后,就需要对这些文件进行合并。

	ComPact的作用就是:合并文件;清理过期,多余版本数据;提高读写效率。

	Hbase中实现了两种Compact 方式:MinorMajor 

	Minor 操作只用来做部分文件的合并操作,不做任何删除数据、多版本数据的清理工作。

	major操作是对Region下的HStore下的所有StoreFile执行合并操作,最终的结果是整理合并出一个文件,会清理过期,多余版本数据。
十二、每天百亿的数据存入Hbase,如何保证数据的存储正确和在规定的时间里全部录入完毕
需求分析
1)百亿数据:证明数据量非常大;
2)存入Hbase:证明与Hbase的写入数据有关
3)保证数据的正确性:要设计正确的数据结构保证正确性
4)在规定的时间内完成:对存入速度有要求
结局思路:
1)百亿数据意味着,假设一整天60*60*24=86400秒都在写入数据,则每秒数据的写入量高达100万条。Hbase当然不支持每秒百万条的数据写入。所以对于百万条的数据写入不可能通过实时写入,只能通过批量导入。批量导入推荐使用BulkLoad,性能是普通写入的几倍以上。
2)存入Hbase:普通写入是使用JAVA API 的PUT 来实现的,批量导入推荐使用BULKLoad;
3) 保证数据的正确性:这里考虑RowKey的设计,预建分区 和 列族设计。
4)在规定时间内完成也就意味着存入速度不能过慢,当然越快越好,使用BulkLoadBulkLoad方法能够将数据快速的load到HBase中,打一个“生动”的比方:
使用API就好比将饭一口一口喂给HBase,而使用BulkLoad就相当于切开HBase的肚子直接将食物放到胃中    
*十三、Hbase 优化的方法
1)减少调整:Hbase可以调整RegionHfileRegion:如果没有预建分区的话,随着Region中数据条目的增加,Region 就会分裂,这将增加IO开销。解决方法是根据RowKey的设计进行预建分区,减少Region的动态分裂。
HFile:HFile 会随着MemStore进行刷新时生成一个HFile,HFile 达到了一定量的时候,会将属于一个RegionHfile 进行合并。如果合并后的HFile 大小大于预设值,那么Hfile 就会进行重新分裂,这样就会导致IO的开销增大。为了减少这样的无谓的I/O开销,建议估计项目数据量大小,给HFile设定一个合适的值。

2)减少启动:
  关闭自动Compaction,在闲时进行手动合并:对于Hbase会有Compact 机制,会合并HFile,大量的HFile 合并肯定会带来IO开销。因此建议关闭自动ComPact,在闲时进行手动合并。
  批量数据写入时采用BulKLoad

3)减少数据量:
   开启过滤,提高查询速度:开启Bloom Filter,这是列族级别的过滤,在生成StoreFile文件时,同时会生成一个MetaBlock,用于查询时过滤)
   使用压缩:一般使用Snappy 和 LZO 压缩
4)合理设计: 
  rowKey设计应该具备以下属性:
  散列性:建议将RowKey的高位作为散列字段,由程序循环生成;低位放时间字段。这样将提高数据均衡分布每个Regionserver,实现RegionServer负载均衡的几率。
  简短性:数据的持久化文件HFile是按照KeyValue存储的,如果RowKey太长的话就会影响HFile的存储效率。
  唯一性:必须在设计上保证RowKey的唯一性。
  业务性:如果我们的查询条件比较多,而且不是针对列的条件。那么Rowkey的设计应该支持多条件查询。如果我的查询要求是最近插入的数据优先,那么rowKey则可以采用叫上Long.Max-时间戳的方式,这样rowKey就是递减排列。

  列族的设计:列族的设计需要看应用场景
  	优势:Hbase中数据是按照列进行存储的,那么查询某一列族的某一列时就不需要扫描全表,只需要扫描某一列族即可,减少了读IO	
  	劣势:降低了写IO的性能。原因同一个Region中存在多个列族,则存在多个Store。每个Store都是一个MemStore.MemStore 进行Flush 时,属于同一个RegionStore 中的MemStore 都会进行Flash,增加了IO开销
十四、Region 如何预建分区
	预建分区的目的主要是创建表的时候提前规划的表有多少分区,以及每个分区的区间范围。这样在存储的时候RowKey可以按照区间进行存储,减少Rigion分裂。
方案一:shell 方法,create 'tb_splits', {NAME => 'cf',VERSIONS=> 3},{SPLITS => ['10','20','30']} 
方案二:JAVA 程序控制,HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][]splitkeys)可以指定预分区的splitKey,即是指定region间的rowkey临界值。
十五、RegionServer 宕机如何处理
1、ZK会监控RegionServer的上下线情况,当ZK发现某个Regionserver宕机之后会通知Master进行故障恢复

2Hmaster 会将该RegionServer 所负责的Region转移到其他的RegionServer上,并对RegionServer上存在MemStore中还未持久化到磁盘的数据进行恢复。

3、恢复过程通过WAL的重播来完成,Wal时一个文件,存在于/Hbase/WAL/对应的RegionServer下;发生宕机时,读取该RegionServer 所对应的路径下的Wal,并"根据不同的Region 切分成不同的临时文件recover.edits";当region 被分配到新的Regionserver中,RegionServer读取Region时判断是否存在recover.edits,如果有则进行恢复。   
十六、Hbase 读写流程
读数据
1Client 先从缓存中定位region,如果没有缓存则需访问zookeeper获取 hbase:meta 表位于哪个 Region Server2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)分别在 Block Cache(读缓存),MemStoreStore FileHFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
5) 将从文件中查询到的数据块(BlockHFile 数据存储单元,默认大小为 64KB)缓存到Block Cache6)将合并后的最终结果返回给客户端。
写数据
1Client 先从缓存中定位region,如果没有缓存则需访问zookeeper获取 hbase:meta 表位于哪个 Region Server2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,
查询出目标数据位于哪个 Region Server 中的哪个 Region 中。(找到小于rowkey并且最接近rowkey的startkey对应的region)
并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)将数据顺序写入(追加)到 WAL;
5)将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
6)向客户端发送 ack;
7)等达到 MemStore 的刷写时机后,将数据刷写到 HFile
*十七、HBase内部机制是什么?
物理存储:hbase的持久化数据是将数据存储在HDFS上。 
存储管理:一个表是划分为很多region的,这些region分布式地存放在很多regionserver上Region内部还可以划分为store,store内部有memstore和storefile。
版本管理:hbase中的数据更新本质上是不断追加新的版本,通过compact操作来做版本间的文件合并。
集群管理:ZooKeeper + HMaster + HRegionServer
十八、Hbase中的memstore是用来做什么的?
	MemStore:俗称写缓存,存放在内存中,用于保存修改的数据。当MemStore的大小达到一定阈值默认为64M,就会被FlushHFile中。由于HFile 要求数据是有序的,所以需要在MemStore中对Key-value数据进行排序,等待刷写时机进行刷写到Hfile。每次刷写都会生成一个Hfile 文件。这样能充分利用hadoop写入大文件的性能优势,提高写入性能。
十九、HBase在进行模型设计时重点在什么地方?一张表中定义多少个Column Family最合适?为什么?
	设计的重点为ColumnFamily.Column Family的个数具体看表的数据,一般来说划分标准是根据数据访问频度,如一张表里有些列访问相对频繁,而另一些列访问很少,这时可以把这张表划分成两个列族,分开存储,提高访问效率。
二十、如何提高Hbase 客户端读写性能
1、开启Bloom 过滤器,开启Bloom filter 比没开启快34Bloom Filter:主要功能是提高随机读的性能。
	存储开销:"bloom filter的数据存在StoreFile的meta中""一旦写入无法更新,由于StoreFile是不可变的""Bloomfilter是一个列族(cf)级别的配置属性",假设你在表中设置了Bloomfilter,那么HBase会在生成StoreFile时包括一份bloomfilter结构的数据,称其为MetaBlockMetaBlockDataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启"bloomfilter会有一定的存储及内存cache开销"2Hbase 对于内存有特别的要求,在硬件允许的情况下分配足够多的内存。

3、通过修改hbase-env.sh中的 export HBASE_HEAPSIZE=3000  #这里默认为1000m 


4、增大RPC数量,通过修改hbase-site.xml 中的hbase.regionservder.handler.count 属性,可以适当放大RPC 数量。
二十一、直接将时间戳作为行健,在写入单个region 时候会发生热点问题,为什么呢?
region中数据是按照rowkey有序存储,若时间比较集中。就会存储到一个region中,这样一个region的数据变多,其它的region数据很少,加载数据就会很慢,直到region分裂,此问题才会得到缓解。
二十三、Hbase 的过滤器
过滤器分为两大类:比较过滤器和专用过滤器

比较过滤器: 行键过滤器、列族过滤器、列过滤器、值过滤器
专用过滤器: 单列值过滤器 SingleColumnValueFilter ----会返回满足条件的整行
	       单列值排除器 SingleColumnValueExcludeFilter -----返回排除了该列的结果 与上面的结果相反
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值