总结《HBase原理与实践》第五章

目录

一、RegionServer的核心模块

1.1RegionServer内部结构

1.2HLog

1.3 MemStore

1.3.1 MemStore的GC问题

1.3.2 MSLAB内存管理方式

1.3.3 MemStore Chunk Pool

1.4 HFile

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

1.5 BlockCache

1.5.1 LRU缓存机制

1.5.2 SlabCache(已经不建议使用)

1.5.3 BucketCache


一、RegionServer的核心模块

RegionServer是Hbase最核心的组件,负责用户数据写入、读取、等基础操作。

1.1RegionServer内部结构

RegionServer是Hbase系统响应用户读写请求的工作节点组件,有以下几个核心模块。

有一个或多个HLog,一个BlockCache以及多个Region组成。

一个Region由多个Store组成,一个store是一个列簇,一个store由1个memstore和多个Hfile组成。

在这里插入图片描述

1.2HLog

Hbase系统故障恢复主从复制都是基于HLog来实现的,默认任何操作都会先写入HLog,再将数据写入MemStore,如果此时系统发生故障,那么Memstore里的数据还未来得及写入HFile,导致数据丢失,就会通过HLog来进行回放。

HBase主从复制需要主集群将HLog日志发送给从集群,从集群在本地执行回放操作,完成集群之间的数据复制。

  • 每个RegionServer有1个或多个HLog,默认只有1个,Region共享HLog。
  • HLog会日志滚动。写入的数据一旦落盘,对用的日志就会失效,HLog文件中的所有数据都已经完成落盘,就会从WALs文件夹移动到了oldWALs文件夹,此时并没有删除。
  • 该HLog文件是否还在参与主从复制。
  • 该HLog文件是否在oldWALs日志存在10分钟。

1.3 MemStore

HBase基于LSM树模型实现,所有数据写入和操作首先顺序写入日志HLog,再写入MemStore,当MemStore超过一定阈值(时间,大小)就会将数据写入磁盘。

优势:

  • 所有写入都是顺序IO写入
  • HFile中keyvalue数据按照Key排序,排序之后可以在文件级别根据有序的key建立索引树,极大提升顺序读写。
  • MemStore作为缓存组件,缓存中最新写入的数据,有极大可能会被读取。

MemStore中使用的是concurrentSkipListMap,写入、查找、删除都是O(logN)的时间复杂度.

特点:线程安全,他在底层采用了CAS算法,保证了原子性

MemStore由两个concurrentSkipListMap来实现,当第一个超过一定的阈值,会新建一个concurrentSkipListMap B,B用来接收, A会执行异步f lush操作落盘形成HFile。

1.3.1 MemStore的GC问题

MemStore从本质上来看就是一块缓存,可以称为写缓存。众所周知在Java系统中,大内存系统总会面临GC问题

HBase中MemStore工作模式的特殊性更会引起严重的内存碎片,存在大量内存碎片会导致系统看起来似乎还有很多空间,但实际上这些空间都是一些非常小的碎片,已经分配不出一块完整的可用内存,这时会触发长时间的Full GC。

原因:HBase的RegionServer包含多个region,每个region根据列簇又会有多个MemStore,这些MemStore是共享内存的,这样,不同Region的数据写入对应的MemStore,因为共享内存,在JVM看来所有MemStore的数据都是混合在一起写入Heap的。

随着MemStore中数据的不断写入并且flush,整个JVM将会产生大量越来越小的内存条带,这些条带实际上就是内存碎片。随着内存碎片越来越小,最后甚至分配不出来足够大的内存给写入的对象,此时就会触发JVM执行FullGC合并这些内存碎片。

1.3.2 MSLAB内存管理方式

HBase借鉴了线程本地分配缓存(Thread-Local Allocation Buffer,TLAB)的内存管理方式,通过顺序化分配内存、内存数据分块等特性使得内存碎片更加粗粒度,有效改善Full GC情况。

参考:https://blog.csdn.net/dnc8371/article/details/106701220/

1.3.3 MemStore Chunk Pool

比如一旦一个Chunk写满之后,系统会重新申请一个新的Chunk,新建Chunk对象会在JVM新生代申请新内存,如果申请比较频繁会导致JVM新生代Eden区满掉,触发YGC。试想如果这些Chunk能够被循环利用,系统就不需要申请新的Chunk,这样就会使得YGC频率降低,晋升到老年代的Chunk就会减少,CMS GC发生的频率也会降低。

1.4 HFile

MemStore中数据落盘之后会形成一个文件写入HDFS,这个文件称为HFile。

  • Scanned Block 表示顺序扫描HFile时所有的数据块将会被读取。DataBlock 存储用户key-value数据,Leaf Index Block存储索引树的叶子节点数据,Bloom Block中存储布隆过滤器相关数据。
  • Non-scanned Block 这部分主要存储Meta Block,这种Block大多数情况下可以不用关心。
  • Load-on-open 在RegionServer打开HFile就会加载到内存,作为查询的入口。 主要存储HFile元数据信息,包括索引根节点、布隆过滤器元数据等
  • Trailer 部分主要记录了HFile的版本信息、其他各个部分的偏移值和寻址信息。

HFile文件由各种不同类型的Block(数据块)构成,虽然这些Block的类型不同,但却拥有相同的数据结构。

HBase在读HFile时会加载所有HFile的Trailer部分以及load-on-open部分到内存中。

写HFile从上至下写,先写用户keyvalue数据,根据keyvalue信息生成叶子索引信息和布隆过滤器,在根据叶子索引信息和布隆过滤器生成对应的元数据信息,在封装HFile的版本信息,keyvalue条数,根据上述的信息生成各个偏移量和寻址信息。这个文件的写入由上至下,没有发生过随机写。

读HFile从下至上读,RegionServer在启动时会自动读Trailer和Load-on-open部分,先读Trailer的版本信息,读取压缩格式,元数据信息等。

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

HBase会为每个HFile分配对应的位数组,KeyValue在写入HFile时会先对Key经过多个hash函数的映射,映射后将对应的数组位置为1,get请求进来之后再使用相同的hash函数对待查询Key进行映射,如果在对应数组位上存在0,说明该get请求查询的Key肯定不在该HFile中。

问题:HFile文件越大,里面存储的KeyValue值越多,位数组就会相应越大。一旦位数组太大就不适合直接加载到内存了。

根据Key进行拆分,一部分连续的Key使用一个位数组。这样,一个HFile中就会包含多个位数组,根据Key进行查询时,首先会定位到具体的位数组,只需要加载此位数组到内存进行过滤即可,从而降低了内存开销。

然后一个HFile会产生多个布隆过滤器,也会有布隆过滤器索引。

随着HFile文件越来越大,Data Block越来越多,索引数据也越来越大,已经无法全部加载到内存中,多级索引可以只加载部分索引,从而降低内存使用空间。同布隆过滤器内存使用问题一样。

1.5 BlockCache

提升数据库读取性能的一个核心方法是,尽可能将热点数据存储到内存中,以避免昂贵的IO开销。

HBase也实现了一种读缓存结构——BlockCache。BlockCache主要用来缓存Block,Block是Hbase中读取的最小单元。

BlockCache是RegionServer级别的,随着RegionServer的启动完成BlockCache的初始化。BlockCache机制有如下几种:

  • LRU(默认的)将数据放在JVM堆中
  • SlabCache(使用较少) 允许堆外内存 为了缓解GC
  • BucketCache 允许堆外内存 为了缓解GC

1.5.1 LRU缓存机制

ConcurrentHashMap来维护key-value,将最近最少使用的Block置换出来。

注意以下几点:

  1. 缓存分层策略 随机读写放在一个区,经常被读的放在一个区,常驻内存的放在一个区(如元数据信息)
  2. 容易引发GC,因为缓存分层策略的机制,导致老年代数据增多,容易引发GC,而且CMS算法会产生大量的内存碎片。

1.5.2 SlabCache(已经不建议使用)

SlabCache中固定大小内存设置会导致实际内存使用率比较低,而且使用LRUBlockCache缓存Block依然会因为JVM GC产生大量内存碎片。

1.5.3 BucketCache

一种Bucket存储一种指定BlockSize的Data Block,BucketCache会在初始化的时候申请多种不同大小的Bucket。Hbase在启动后决定了标签的分类,有4k,16K,64K,128K,512K等,按照block大小来分配Bucket,

BucketCache通过不同配置方式可以工作在三种模式下:

  • heap (heap模式需要首先从操作系统分配内存再拷贝到JVM heap
  • offheap (使用JAVA NIO DirectByteBuffer实现堆外内存存储管理)
  • file 使用类似SSD的存储介质来缓存Data Block

offheap 和 heap的优缺点:

内存分配:offerheap是操作系统分配内存,降低了GC概率,heap是操作系统分配内存在拷贝到jvm heap。

读取缓存:heap直接从jvm heap上读取,offheap模式则需要首先从操作系统拷贝到JVMheap再读取,因此更费时。

实际实现中,HBase将BucketCacheLRUBlockCache搭配使用,称为CombinedBlock-Cache。

LRUBlockCache缓存 index Block 和 BloomBlock

BucketCache缓存DataBlock

因此一次随机读需要先在LRUBlockCache中查到对应的Index Block,然后再到BucketCache查找对应Data Block。

原文地址:https://www.cnblogs.com/erlou96/p/14202496.html