Hbase RegionServer的核心模块

news2024/11/20 7:17:28

RegionServer是HBase系统中最核心的组件,主要负责用户数据写入、读取等基础操作。RegionServer组件实际上是一个综合体系,包含多个各司其职的核心模块:HLog、MemStore、HFile以及BlockCache。

一、RegionServer内部结构

RegionServer是HBase系统响应用户读写请求的工作节点组件,由多个核心模块组成。
在这里插入图片描述
一个RegionServer由一个(或多个)HLog、一个BlockCache以及多个Region组成。其中,HLog用来保证数据写入的可靠性;BlockCache可以将数据块缓存在内存中以提升数据读取性能;Region是HBase中数据表的一个数据分片,一个RegionServer上通常会负责多个Region的数据读写。一个Region由多个Store组成,每个Store存放对应列簇的数据,比如一个表中有两个列簇,这个表的所有Region就都会包含两个Store。每个Store包含一个MemStore和多个HFile,用户数据写入时会将对应列簇数据写入相应的MemStore,一旦写入数据的内存大小超过设定阈值,系统就会将MemStore中的数据落盘形成HFile文件。HFile存放在HDFS上,是一种定制化格式的数据存储文件,方便用户进行数据读取。

二、HLog

HBase中系统故障恢复以及主从复制都基于HLog实现。默认情况下,所有写入操作(写入、更新以及删除)的数据都先以追加形式写入HLog,再写入MemStore。大多数情况下,HLog并不会被读取,但如果RegionServer在某些异常情况下发生宕机,此时已经写入MemStore中但尚未flush到磁盘的数据就会丢失,需要回放HLog补救丢失的数据。此外,HBase主从复制需要主集群将HLog日志发送给从集群,从集群在本地执行回放操作,完成集群之间的数据复制。

1,HLog文件结构

在这里插入图片描述
(1)每个RegionServer拥有一个或多个HLog(默认只有1个,1.1版本可以开启MultiWAL功能,允许多个HLog)。每个HLog是多个Region共享的,图5-2中Region A、Region B和Region C共享一个HLog文件。
(2)HLog中,日志单元WALEntry(图中小方框)表示一次行级更新的最小追加单元,它由HLogKey和WALEdit两部分组成,其中HLogKey由table name、region name以及sequenceid等字段构成。

WALEdit用来表示一个事务中的更新集合,在0.94之前的版本中,如果一个事务对一行row R三列c1、c2、c3分别做了修改,那么
HLog中会有3个对应的日志片段,如下所示:

<logseq1-for-edit1>:<keyvalue-for-edit-c1>
<logseq2-for-edit2>:<keyvalue-for-edit-c2>
<logseq3-for-edit3>:<keyvalue-for-edit-c3>

然而,这种日志结构无法保证行级事务的原子性,假如RegionServer更新c2列之后发生宕机,那么一行记录中只有部分数据写入成功。为了解决这样的问题,HBase将一个行级事务的写入操作表示为一条记录,如下所示:

<logseq#-for-entire-txn>:<WALEdit-for-entire-txn>

其中,WALEdit会被序列化为格式<-1,#of edits,,,>,比如<-1,3,,,>,-1为标识符,表示这种新的日志结构。

2,HLog文件存储

HBase中所有数据(包括HLog以及用户实际数据)都存储在HDFS的指定目录(假设为hbase-root)下,可以通过hadoop命令查看hbase-root目录下与HLog有关的子目录,如下所示:

drwxr-xr-x   - hadoop hadoop          0 2017-09-21 17:12 /hbase/WALs
drwxr-xr-x   - hadoop hadoop          0 2017-09-22 06:52 /hbase/oldWALs

其中,/hbase/WALs存储当前还未过期的日志;/hbase/oldWALs存储已经过期的日志。可以进一步查看/hbase/WALs目录下的日志文件,如下所示:

/hbase/WALs/hbase17.xj.bjbj.org,60020,1505980274300
/hbase/WALs/hbase18.xj.bjbj.org,60020,1505962561368
/hbase/WALs/hbase19.xj.bjbj.org,60020,1505980197364

/hbase/WALs目录下通常会有多个子目录,每个子目录代表一个对应的RegionServer。以hbase17.xj.bjbj.org,60020,1505980274300为例,hbase17.xj.bjbj.org表示对应的RegionServer域名,60020为端口号,1505980274300为目录生成时的时间戳。每个子目录下存储该RegionServer内的所有HLog文件,如下所示:

/hbase/WALs/hbase17.xj.bjbj.org,60020,1505980274300/hbase17.xj.bjbj.org%2C60020%2C1505980274300.default.1506184980449

HLog文件为:

hbase17.xj.bjbj.org%2C60020%2C1505980274300.default.1506012772205

3,HLog生命周期

HLog文件生成之后并不会永久存储在系统中,它的使命完成后,文件就会失效最终被删除。在这里插入图片描述
HLog生命周期包含4个阶段:
(1)HLog构建:HBase的任何写入(更新、删除)操作都会先将记录追加写入到HLog文件中。
(2)HLog滚动:HBase后台启动一个线程,每隔一段时间(由参数’hbase.regionserver.logroll.period’决定,默认1小时)进行日志滚动。日志滚动会新建一个新的日志文件,接收新的日志数据。日志滚动机制主要是为了方便过期日志数据能够以文件的形式直接删除。
(3)HLog失效:写入数据一旦从MemStore中落盘,对应的日志数据就会失效。为了方便处理,HBase中日志失效删除总是以文件为单位执行。查看某个HLog文件是否失效只需确认该HLog文件中所有日志记录对应的数据是否已经完成落盘,如果日志中所有日志记录已经落盘,则可以认为该日志文件失效。一旦日志文件失效,就会从WALs文件夹移动到oldWALs文件夹。注意此时HLog并没有被系统删除。
(4)HLog删除:Master后台会启动一个线程,每隔一段时间(参数’hbase.master.cleaner.interval’,默认1分钟)检查一次文件夹oldWALs下的所有失效日志文件,确认是否可以删除,确认可以删除之后执行删除操作。

注意:
确认HLog是否删除的条件有两个:
(1)该HLog文件是否还在参与主从复制。对于使用HLog进行主从复制的业务,需要继续确认是否该HLog还在应用于主从复制。
(2)该HLog文件是否已经在OldWALs目录中存在10分钟。为了更加灵活地管理HLog生命周期,系统提供了参数设置日志文件的TTL(参数’hbase.master.logcleaner.ttl’,默认10分钟),默认情况下oldWALs里面的HLog文件最多可以再保存10分钟。

三、MemStore

HBase系统中一张表会被水平切分成多个Region,每个Region负责自己区域的数据读写请求。水平切分意味着每个Region会包含所有的列簇数据,HBase将不同列簇的数据存储在不同的Store中,每个Store由一个MemStore和一系列HFile组成,如图5-4所示。
HBase基于LSM树模型实现,所有的数据写入操作首先会顺序写入日志HLog,再写入MemStore,当MemStore中数据大小超过阈值之后再将这些数据批量写入磁盘,生成一个新的HFile文件。

LSM树架构的优势:
(1)这种写入方式将一次随机IO写入转换成一个顺序IO写入(HLog顺序写入)加上一次内存写入(MemStore写入),使得写入性能得到极大提升。大数据领域中对写入性能有较高要求的数据库系统几乎都会采用这种写入模型,比如分布式列式存储系统Kudu、时间序列存储系统Druid等。
(2)HFile中KeyValue数据需要按照Key排序,排序之后可以在文件级别根据有序的Key建立索引树,极大提升数据读取效率。然而HDFS本身只允许顺序读写,不能更新,因此需要数据在落盘生成HFile之前就完成排序工作,MemStore就是KeyValue数据排序的实际执行者。
(3)MemStore作为一个缓存级的存储组件,总是缓存着最近写入的数据。对于很多业务来说,最新写入的数据被读取的概率会更大,最典型的比如时序数据,80%的请求都会落到最近一天的数据上。实际上对于某些场景,新写入的数据存储在MemStore对读取性能的提升至关重要。
(4)在数据写入HFile之前,可以在内存中对KeyValue数据进行很多更高级的优化。比如,如果业务数据保留版本仅设置为1,在业务更新比较频繁的场景下,MemStore中可能会存储某些数据的多个版本。这样,MemStore在将数据写入HFile之前实际上可以丢弃老版本数据,仅保留最新版本数据。
在这里插入图片描述

1,MemStore内部结构

写入(包括更新删除操作)HBase中的数据都会首先写入MemStore,除此之外,MemStore还要承担业务多线程并发访问的职责。那么一个很现实的问题就是,MemStore应该采用什么样的数据结构,既能够保证高效的写入效率,又能够保证高效的多线程读取效率?
实际实现中,HBase采用了跳跃表这种数据结构,关于跳跃表的基础知识,2.1节进行了详细介绍。当然,HBase并没有直接使用原始跳跃表,而是使用了JDK自带的数据结构ConcurrentSkipListMap。ConcurrentSkipListMap底层使用跳跃表来保证数据的有序性,并保证数据的写入、查找、删除操作都可以在O(logN)的时间复杂度完成。除此之外,ConcurrentSkipListMap有个非常重要的特点是线程安全,它在底层采用了CAS原子性操作,避免了多线程访问条件下昂贵的锁开销,极大地提升了多线程访问场景下的读写性能。
MemStore由两个ConcurrentSkipListMap(称为A和B)实现,写入操作(包括更新删除操作)会将数据写入ConcurrentSkipListMap A,当ConcurrentSkipListMap A中数据量超过一定阈值之后会创建一个新的ConcurrentSkipListMap B来接收用户新的请求,之前已经写满的ConcurrentSkipListMap A会执行异步flush操作落盘形成HFile。

2,MemStore的GC问题

MemStore从本质上来看就是一块缓存,可以称为写缓存。众所周知在Java系统中,大内存系统总会面临GC问题,MemStore本身会占用大量内存,因此GC的问题不可避免。不仅如此,HBase中MemStore工作模式的特殊性更会引起严重的内存碎片,存在大量内存碎片会导致系统看起来似乎还有很多空间,但实际上这些空间都是一些非常小的碎片,已经分配不出一块完整的可用内存,这时会触发长时间的Full GC。
为什么MemStore的工作模式会引起严重的内存碎片?这是因为一个RegionServer由多个Region构成,每个Region根据列簇的不同又包含多个MemStore,这些MemStore都是共享内存的。这样,不同Region的数据写入对应的MemStore,因为共享内存,在JVM看来所有MemStore的数据都是混合在一起写入Heap的。

3,MSLAB内存管理方式

为了优化这种内存碎片可能导致的Full GC,HBase借鉴了线程本地分配缓存(Thread-Local Allocation Buffer,TLAB)的内存管理方式,通过顺序化分配内存、内存数据分块等特性使得内存碎片更加粗粒度,有效改善Full GC情况。具体实现步骤如下:
(1)每个MemStore会实例化得到一个MemStoreLAB对象。
(2)MemStoreLAB会申请一个2M大小的Chunk数组,同时维护一个Chunk偏移量,该偏移量初始值为0。
(3)当一个KeyValue值插入MemStore后,MemStoreLAB会首先通过KeyValue.getBuffer()
取得data数组,并将data数组复制到Chunk数组中,之后再将Chunk偏移量往前移动data.length。
(4)当前Chunk满了之后,再调用new byte[210241024]申请一个新的Chunk。
这种内存管理方式称为MemStore本地分配缓存(MemStore-Local Allocation Buffer,MSLAB)。

4,MemStore Chunk Pool

经过MSLAB优化之后,系统因为MemStore内存碎片触发的Full GC次数会明显降低。然而这样的内存管理模式并不完美,还存在一些“小问题”。比如一旦一个Chunk写满之后,系统会重新申请一个新的Chunk,新建Chunk对象会在JVM新生代申请新内存,如果申请比较频繁会导致JVM新生代Eden区满掉,触发YGC。试想如果这些Chunk能够被循环利用,系统就不需要申请新的Chunk,这样就会使得YGC频率降低,晋升到老年代的Chunk就会减少,CMS GC发生的频率也会降低。这就是MemStore Chunk Pool的核心思想,具体实现步骤如下:
(1)系统创建一个Chunk Pool来管理所有未被引用的Chunk,这些Chunk就不会再被JVM当作垃圾回收。
(2)如果一个Chunk没有再被引用,将其放入Chunk Pool。
(3)如果当前Chunk Pool已经达到了容量最大值,就不会再接纳新的Chunk。
(4)如果需要申请新的Chunk来存储KeyValue,首先从Chunk Pool中获取,如果能够获取得到就重复利用,否则就重新申请一个新的Chunk。

5,MSLAB相关配置

HBase中MSLAB功能默认是开启的,默认的ChunkSize是2M,也可以通过参数"hbase.hregion.memstore.mslab.chunksize"进行设置,建议保持默认值。
Chunk Pool功能默认是关闭的,需要配置参数"hbase.hregion.memstore.chunkpool.maxsize"为大于0的值才能开启,该值默认是0。"hbase.hregion.memstore.chunkpool.maxsize"取值为[0,1],表示整个MemStore分配给Chunk Pool的总大小为hbase.hregion.memstore.chunkpool.
maxsize*Memstore Size。另一个相关参数"hbase.hregion.memstore.chunkpool.initialsize"取值为[0,1],表示初始化时申请多少个Chunk放到Pool里面,默认是0,表示初始化时不申请内存。

四、HFile

MemStore中数据落盘之后会形成一个文件写入HDFS,这个文件称为HFile。HFile参考BigTable的SSTable和Hadoop的TFile实现。从HBase诞生到现在,HFile经历了3个版本,其中V2在0.92引入,V3在0.98引入。HFile V1版本在实际使用过程中发现占用内存过多,HFile V2版本针对此问题进行了优化,HFile V3版本和V2版本基本相同,只是在cell层面添加了对Tag数组的支持。

1,HFile逻辑结构

在这里插入图片描述
HFile文件主要分为4个部分:Scanned block部分、Non-scanned block部分、Load-on-open部分和Trailer。
(1)Scanned Block部分:顾名思义,表示顺序扫描HFile时所有的数据块将会被读取。这个部分包含3种数据块:Data Block,Leaf Index Block以及Bloom Block。其中Data Block中存储用户的KeyValue数据,Leaf Index Block中存储索引树的叶子节点数据,Bloom Block中存储布隆过滤器相关数据。
(2)Non-scanned Block部分:表示在HFile顺序扫描的时候数据不会被读取,主要包括Meta Block和Intermediate Level Data Index Blocks两部分。
(3)Load-on-open部分:这部分数据会在RegionServer打开HFile时直接加载到内存中,包括FileInfo、布隆过滤器MetaBlock、Root Data Index和Meta IndexBlock。
(4)Trailer部分:这部分主要记录了HFile的版本信息、其他各个部分的偏移值和寻址信息。

2,HFile物理结构

在这里插入图片描述
HFile文件由各种不同类型的Block(数据块)构成,虽然这些Block的类型不同,但却拥有相同的数据结构。
Block的大小可以在创建表列簇的时候通过参数blocksize=>'65535’指定,默认为64K。通常来讲,大号的Block有利于大规模的顺序扫描,而小号的Block更有利于随机查询。因此用户在设置blocksize时需要根据业务查询特征进行权衡,默认64K是一个相对折中的大小。
HFileBlock主要包含两部分:BlockHeader和BlockData。其中BlockHeader主要存储Block相关元数据,BlockData用来存储具体数据。Block元数据中最核心的字段是BlockType字段,表示该Block的类型,HBase中定义了8种BlockType,每种BlockType对应的Block都存储不同的内容,有的存储用户数据,有的存储索引数据,有的存储元数据(meta)。对于任意一种类型的HFileBlock,都拥有相同结构的BlockHeader,但是BlockData结构却一定相同。
在这里插入图片描述

3,HFile的基础Block

(1)Trailer Block

Trailer Block主要记录了HFile的版本信息、各个部分的偏移值和寻址信息,图5-11为Trailer Block的数据结构,其中只显示了部分核心字段。
RegionServer在打开HFile时会加载所有HFile的Trailer部分以及load-on-open部分到内存中。实际加载过程会首先会解析Trailer Block,然后再进一步加载load-on-open部分的数据,具体步骤如下:
1)加载HFile version版本信息,HBase中version包含majorVersion和minorVersion两部分,前者决定了HFile的主版本——V1、V2还是V3;后者在主版本确定的基础上决定是否支持一些微小修正,比如是否支持checksum等。不同的版本使用不同的文件解析器对HFile进行读取解析。
2)HBase会根据version信息计算Trailer Block的大小(不同version的Trailer Block大小不同),再根据Trailer Block大小加载整个HFileTrailer Block到内存中。Trailer Block中包含很多统计字段,例如,TotalUncompressedBytes表示HFile中所有未压缩的KeyValue总大小。NumEntries表示HFile中所有KeyValue总数目。Block中字段CompressionCodec表示该HFile所使用的压缩算法,HBase中压缩算法主要有lzo、gz、snappy、lz4等,默认为none,表示不使用压缩。
3)Trailer Block中另两个重要的字段是LoadOnOpenDataOffset和LoadOnOpenDataSize,前者表示load-on-open Section在整个HFile文件中的偏移量,后者表示load-on-open Section的大小。根据此偏移量以及大小,HBase会在启动后将load-on-open Section的数据全部加载到内存中。load-on-open部分主要包括FileInfo模块、Root Data Index模块以及布隆过滤器Metadata模块,FileInfo是固定长度的数据块,主要记录了文件的一些统计元信息,比较重要的是AVG_KEY_LEN和AVG_VALUE_LEN,分别记录了该文件中所有Key和Value的平均长度。Root Data Index表示该文件数据索引的根节点信息,布隆过滤器Metadata记录了HFile中布隆过滤器的相关元数据。
在这里插入图片描述

(3)Data Block

Data Block是HBase中文件读取的最小单元。Data Block中主要存储用户的KeyValue数据,而KeyValue结构是HBase存储的核心。HBase中所有数据都是以KeyValue结构存储在HBase中。
在这里插入图片描述
KeyValue由4个部分构成,分别为Key Length、Value Length、Key和Value。其中,Key Length和Value Length是两个固定长度的数值,Value是用户写入的实际数据,Key是一个复合结构,由多个部分构成:Rowkey、Column Family、Column Qualifier、TimeStamp以及KeyType。其中,KeyType有四种类型,分别是Put、Delete、DeleteColumn和DeleteFamily。
由Data Block的结构可以看出,HBase中数据在最底层是以KeyValue的形式存储的,其中Key是一个比较复杂的复合结构,这点最早在第1章介绍HBase数据模型时就提到过。因为任意KeyValue中都包含Rowkey、Column Family以及Column Qualifier,因此这种存储方式实际上比直接存储Value占用更多的存储空间。这也是HBase系统在表结构设计时经常强调Rowkey、ColumnFamily以及Column Qualifier尽可能设置短的根本原因。

4,HFile中与布隆过滤器相关的Block

布隆过滤器的基本原理可以参考第2章。布隆过滤器对HBase的数据读取性能优化至关重要。前面2.2节介绍过HBase是基于LSM树结构构建的数据库系统,数据首先写入内存,然后异步flush到磁盘形成文件。这种架构天然对写入友好,而对数据读取并不十分友好,因为随着用户数据的不断写入,系统会生成大量文件,用户根据Key获取对应的Value,理论上需要遍历所有文件,在文件中查找指定的Key,这无疑是很低效的做法。使用布隆过滤器可以对数据读取进行相应优化,对于给定的Key,经过布隆过滤器处理就可以知道该HFile中是否存在待检索Key,如果不存在就不需要遍历查找该文件,这样就可以减少实际IO次数,提高随机读性能。布隆过滤器通常会存储在内存中,所以布隆过滤器处理的整个过程耗时基本可以忽略。
HBase会为每个HFile分配对应的位数组,KeyValue在写入HFile时会先对Key经过多个hash函数的映射,映射后将对应的数组位置为1,get请求进来之后再使用相同的hash函数对待查询Key进行映射,如果在对应数组位上存在0,说明该get请求查询的Key肯定不在该HFile中。当然,如果映射后对应数组位上全部为1,则表示该文件中有可能包含待查询Key,也有可能不包含,需要进一步查找确认。
可以想象,HFile文件越大,里面存储的KeyValue值越多,位数组就会相应越大。一旦位数组太大就不适合直接加载到内存了,因此HFile V2在设计上将位数组进行了拆分,拆成了多个独立的位数组(根据Key进行拆分,一部分连续的Key使用一个位数组)。这样,一个HFile中就会包含多个位数组,根据Key进行查询时,首先会定位到具体的位数组,只需要加载此位数组到内存进行过滤即可,从而降低了内存开销。
在文件结构上每个位数组对应HFile中一个Bloom Block,因此多个位数组实际上会对应多个Bloom Block。为了方便根据Key定位对应的位数组,HFile V2又设计了相应的索引Bloom IndexBlock

整个HFile中仅有一个Bloom Index Block数据块,位于load-on-open部分。Bloom Index Block(见图5-13左侧部分)从大的方面看由两部分内容构成,其一是HFile中布隆过滤器的元数据基本信息,其二是构建了指向Bloom Block的索引信息。
Bloom Index Block结构中TotalByteSize表示位数组大小,NumChunks表示Bloom Block的个数,HashCount表示hash函数的个数,HashType表示hash函数的类型,TotalKeyCount表示布隆过滤器当前已经包含的Key的数目,TotalMaxKeys表示布隆过滤器当前最多包含的Key的数目。
Bloom Index Entry对应每一个Bloom Block的索引项,作为索引分别指向scanned block部分的Bloom Block,Bloom Block中实际存储了对应的位数组。Bloom Index Entry的结构见图5-13中间部分,其中BlockKey是一个非常关键的字段,表示该Index Entry指向的Bloom Block中第一个执行Hash映射的Key。BlockOffset表示对应Bloom Block在HFile中的偏移量。
因此,一次get请求根据布隆过滤器进行过滤查找需要执行以下三步操作:
1)首先根据待查找Key在Bloom Index Block所有的索引项中根据BlockKey进行二分查找,定位到对应的Bloom Index Entry。
2)再根据Bloom Index Entry中BlockOffset以及BlockOndiskSize加载该Key对应的位数组。
3)对Key进行Hash映射,根据映射的结果在位数组中查看是否所有位都为1,如果不是,表示该文件中肯定不存在该Key,否则有可能存在。

5,HFile中索引相关的Block

根据索引层级的不同,HFile中索引结构分为两种:single-level和multi-level,前者表示单层索引,后者表示多级索引,一般为两级或三级。HFile V1版本中只有single-level一种索引结构,V2版本中引入多级索引。之所以引入多级索引,是因为随着HFile文件越来越大,Data Block越来越多,索引数据也越来越大,已经无法全部加载到内存中,多级索引可以只加载部分索引,从而降低内存使用空间。同布隆过滤器内存使用问题一样,这也是V1版本升级到V2版本最重要的因素之一。
V2版本Index Block有两类:Root Index Block和NonRoot Index Block。NonRoot Index Block又分为Intermediate Index Block和Leaf Index Block两种。HFile中索引是树状结构,RootIndex Block表示索引数根节点,Intermediate Index Block表示中间节点,Leaf Index Block表示叶子节点,叶子节点直接指向实际Data Block,如图5-14所示。
需要注意的是,这三种Index Block在HFile中位于不同的部分,Root Index Block位于“load-on-open”部分,会在RegionServer打开HFile时加载到内存中。Intermediate Index Block位于“Non-Scanned block”部分,Leaf Index Block位于“scanned block”部分。
HFile中除了Data Block需要索引之外,Bloom Block也需要索引,Bloom索引结构实际上采用了单层结构,Bloom Index Block就是一种Root Index Block。
对于Data Block,由于HFile刚开始数据量较小,索引采用单层结构,只有Root Index一层索引,直接指向Data Block。当数据量慢慢变大,Root Index Block大小超过阈值之后,索引就会分裂为多级结构,由一层索引变为两层,根节点指向叶子节点,叶子节点指向实际Data Block。如果数据量再变大,索引层级就会变为三层。

五、BlockCache

提升数据库读取性能的一个核心方法是,尽可能将热点数据存储到内存中,以避免昂贵的IO开销。现代系统架构中,诸如Redis这类缓存组件已经是体系中的核心组件,通常将其部署在数据库的上层,拦截系统的大部分请求,保证数据库的“安全”,提升整个系统的读取效率。

1,LRUBlockCache

LRUBlockCache是HBase目前默认的BlockCache机制,实现相对比较简单。它使用一个ConcurrentHashMap管理BlockKey到Block的映射关系,缓存Block只需要将BlockKey和对应的Block放入该HashMap中,查询缓存就根据BlockKey从HashMap中获取即可。同时,该方案采用严格的LRU淘汰算法,当Block Cache总量达到一定阈值之后就会启动淘汰机制,最近最少使用的Block会被置换出来。

2,SlabCache

为了解决LRUBlockCache方案中因JVM垃圾回收导致的服务中断问题,SlabCache方案提出使用Java NIO DirectByteBuffer技术实现堆外内存存储,不再由JVM管理数据内存。默认情况下,系统在初始化的时候会分配两个缓存区,分别占整个BlockCache大小的80%和20%,每个缓存区分别存储固定大小的Block,其中前者主要存储小于等于64K的Block,后者存储小于等于128K的Block,如果一个Block太大就会导致两个区都无法缓存。和LRUBlockCache相同,SlabCache也使用Least-Recently-Used算法淘汰过期的Block。和LRUBlockCache不同的是,SlabCache淘汰Block时只需要将对应的BufferByte标记为空闲,后续cache对其上的内存直接进行覆盖即可。
线上集群环境中,不同表不同列簇设置的BlockSize都可能不同,很显然,默认只能存储小于等于128KB Block的SlabCache方案不能满足部分用户场景。比如,用户设置BlockSize=256K,简单使用SlabCache方案就不能达到缓存这部分Block的目的。因此HBase在实际实现中将SlabCache和LRUBlockCache搭配使用,称为DoubleBlockCache。在一次随机读中,一个Block从HDFS中加载出来之后会在两个Cache中分别存储一份。缓存读时首先在LRUBlockCache中查找,如果Cache Miss再在SlabCache中查找,此时如果命中,则将该Block放入LRUBlockCache中。

3,BucketCache

SlabCache方案在实际应用中并没有很大程度改善原有LRUBlockCache方案的GC弊端,还额外引入了诸如堆外内存使用率低的缺陷。然而它的设计并不是一无是处,至少在使用堆外内存这方面给予了后续开发者很多启发。站在SlabCache的肩膀上,社区工程师设计开发了另一种非常高效的缓存方案——BucketCache。
BucketCache通过不同配置方式可以工作在三种模式下:heap,offheap和file。heap模式表示这些Bucket是从JVM Heap中申请的;offheap模式使用DirectByteBuffer技术实现堆外内存存储管理;file模式使用类似SSD的存储介质来缓存Data Block。无论工作在哪种模式下,BucketCache都会申请许多带有固定大小标签的Bucket,和SlabCache一样,一种Bucket存储一种指定BlockSize的Data Block,但和SlabCache不同的是,BucketCache会在初始化的时候申请14种不同大小的Bucket,而且如果某一种Bucket空间不足,系统会从其他Bucket空间借用内存使用,因此不会出现内存使用率低的情况。

文章来源:《HBase原理与实践》 作者:胡争;范欣欣

文章内容仅供学习交流,如有侵犯,联系删除哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/388168.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Altium Designer PCB孤岛铜的去除方法教程

孤岛铜&#xff0c;也叫死铜&#xff0c;是指在PCB中孤立无连接的铜箔&#xff0c;一般都是在敷铜的时候产生&#xff0c;不利于生产。解决的办法比较简单&#xff0c;可以手工连线将其与同网络的铜箔相连&#xff0c;也可以通过打过孔的方式将其与同网络的铜箔相连。无法解决的…

Biomod2 (下):物种分布模型建模

这里写目录标题1.给出一个线性回归模型并求出因子贡献度2.biomod22.1 pseudo-absences:伪不存在点&#xff08;PA&#xff09;2.1.1 random2.2.2 disk2.2.3 user.defined method3.使用网格划分区域3.1 计算质心4. 完整案例1.给出一个线性回归模型并求出因子贡献度 ##---------…

【游戏逆向】FPS游戏玩家对象数据分析

玩家健康值 查找玩家健康值,玩家健康值是100,但是我们并不知道数值类型,我们可以使用精确搜索方式搜索100-所有类型 CE搜索 结果很多,我们可以使用手雷来减少血量 我们会得到两个结果 我们可以去尝试改变数值,最终发现一个是我们的客户端健康值,一个是服务器健康值,…

Java——N皇后问题

题目链接 leetcode在线oj题——N皇后 题目描述 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff…

RocketMQ Broker消息处理流程剩余源码解析

&#x1f34a; Java学习&#xff1a;Java从入门到精通总结 &#x1f34a; 深入浅出RocketMQ设计思想&#xff1a;深入浅出RocketMQ设计思想 &#x1f34a; 绝对不一样的职场干货&#xff1a;大厂最佳实践经验指南 &#x1f4c6; 最近更新&#xff1a;2023年3月4日 &#x1…

CCNP350-401学习笔记(补充题目1-100)

1、wireless client to roam --->> wireless client 2、Cisco aWIPS policies on the WLC 3、 import json -->> while true -->>except -->> File open -->>File.close() -->> File.open() 4、 login console group radius 5、undesir…

大数据框架之Hadoop:MapReduce(七)MapReduce扩展案例

一、倒排索引案例&#xff08;多job串联&#xff09; 1、需求 有大量的文本&#xff08;文档、网页&#xff09;&#xff0c;需要建立搜索索引&#xff0c;如图4-31所示。 &#xff08;1&#xff09;数据输入 &#xff08;2&#xff09;期望输出数据 atguigu c.txt–>2…

VS2022+Qt5.14.2成功编译MITK2022.10

目录 一 编译结果 二 编译问题解决 三 参考链接 一 编译结果 二 编译问题解决 error C2220错误 1> mitkSlicedGeometry3D.cpp 1>D:\MITK\src\MITK-2022.10\Modules\Core\src\DataManagement\mitkSlicedGeometry3D.cpp(1,1): error C2220: 以下警告被视为错误 [D:\MI…

能代替try catch处理异常的优雅方式

前言软件开发过程中&#xff0c;不可避免的是需要处理各种异常&#xff0c;就我自己来说&#xff0c;至少有一半以上的时间都是在处理各种异常情况&#xff0c;所以代码中就会出现大量的try {…} catch {…} finally {…} 代码块&#xff0c;不仅有大量的冗余代码&#xff0c;而…

【办公类-19-03】办公中的思考——Python批量统一文件名的序号(保教主任整理打印文件)

背景需求&#xff1a;为迎接督导检查&#xff0c;保教主任从各条线收集文本资料。并在每个文件名称前手动编号。但是她嘀咕道&#xff1a;”为什么两套资料放在一个文件里就不是按照数字序号排序&#xff1f;&#xff0c;有的是1X-&#xff0c;有的是40X&#xff0c;看起来很乱…

20230304 CF855 div3 vp

Dashboard - Codeforces Round 855 (Div. 3) - Codeforces呃呃&#xff0c;评价是&#xff0c;毫无进步呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训了该加训…

有关平方或高次方的公式整理一元高次方程的求解

Part.I Introduction 这篇博文记录一下数学中常用的有关平方或高次方的一些公式。 Chap.I 一些结论 下面一部分汇总了一些重要的结论 完全平方公式&#xff1a;(ab)2a22abb2(ab)^2a^22abb^2(ab)2a22abb2平方差公式&#xff1a;a2−b2(ab)(a−b)a^2-b^2(ab)(a-b)a2−b2(ab)(…

Spring——Bean管理-注解方式进行属性注入

Spring针对Bean管理中创建对象提供的注解有哪些&#xff1f;Component&#xff1a;普通Service&#xff1a;业务逻辑层Controller&#xff1a;controller层Repository&#xff1a;dao层用注解的方式是为什么&#xff1f;简化xml方式开发&#xff0c;只需要注解就可以完成在配置…

【Redis场景5】集群秒杀优化-分布式锁

集群环境下的秒杀问题 前序 【Redis场景1】用户登录注册 【Redis场景2】缓存更新策略(双写一致) 【Redis场景3】缓存穿透、击穿问题 【Redis场景拓展】秒杀问题-全局唯一ID生成策略 【Redis场景4】单机环境下秒杀问题 在单机环境下的并发问题&#xff0c;我们可以使用相关…

39. 实战:基于api接口实现视频解析播放(32接口,窗口化操作,可导出exe,附源码)

目录 前言 目的 思路 代码实现 需要导入的模块 1. 导入解析网站列表&#xff0c;实现解析过程 2. 设计UI界面 3. 设置窗口居中和循环执行 4. 注意事项 完整源码 运行效果 总结 前言 本节将类似34. 实战&#xff1a;基于某api实现歌曲检索与下载&#xff08;附完整…

SpringCloud:Nacos的安装(Windows,Linux)

目录 一、认识和安装Nacos 1、下载 2、点击进入Github&#xff0c;进入Releases 3、点击Tags 4、解压&#xff08;Windows版&#xff09; 5、端口配置 6、启动 7、访问 二、Linux系统安装Nacos 1、打开虚拟机&#xff0c;使用xshell连接虚拟机&#xff0c;Nacos依赖于…

JVM内置锁synchronized关键字详解

目录 JVM内置锁synchronized关键字详解 设计同步器的意义 如何解决线程并发安全问题&#xff1f; synchronized原理详解 synchronized底层原理 synchronized在jdk1.6前后的变化【重点】 jdk小于1.6时 jdk>1.6时 轻量级锁何时升级为重量级锁&#xff1f;&#xff1f;…

【ROS学习笔记10】ROS中配置自定义Cpp头文件和导入自定义Python库

【ROS学习笔记10】ROS中配置自定义Cpp头文件和导入自定义Python库 文章目录【ROS学习笔记10】ROS中配置自定义Cpp头文件和导入自定义Python库一、ROS中的头文件和源文件1.1 自定义头文件调用1.2 自定义源文件调用二、Python模块的导入Reference写在前面&#xff0c;本系列笔记参…

springBoot 启动指定配置文件环境多种方案

springBoot 启动指定配置文件环境理论上是有多种方案的&#xff0c;一般都是结合我们的实际业务选择不同的方案&#xff0c;比如&#xff0c;有pom.xml文件指定、maven命令行指定、配置文件指定、启动jar包时指定等方案&#xff0c;今天我们一一分享一下&#xff0c;以供参考&a…

Java知识复习(十二)Docker

1、容器 一句话概括容器&#xff1a;容器就是将软件打包成标准化单元&#xff0c;以用于开发、交付和部署容器镜像是轻量的、可执行的独立软件包 &#xff0c;包含软件运行所需的所有内容&#xff1a;代码、运行时环境、系统工具、系统库和设置。容器化软件适用于基于 Linux 和…