Hbase工作原理(一) Hbase基本架构

619 阅读6分钟

Flink系列暂时停更一段时间,关于并行度机制 ,TaskManager的数据交换机制,以及flink standalone以及yarn的远程启动流程之后有时间会开篇章讲解。

对于Hbase而言,因为目前这个存储组件并没有涉及到可以二开的地方,所以Hbase系列会围绕官方文档,以及基本架构和常见问题的原理进行讲解

参考文章:developer.ibm.com/zh/technolo…

Hbase基本结构

风轮-Hbase基本结构.jpg

个人架构图: www.processon.com/view/link/6…

1. 每个RegionServer拥有一个或多个HLog。(默认一个,1.1版本可开启MultiWAL功能,允许多个HLog2. 每个HLog多个Region共享。
3. 日志单元WALEntry表示一次行级更新的最小追加单元,由HLogKeyWALEdit两部分组成。
4. WALEdit表示一个事务中的更新集合。
        0.94之前版本中,如果一个事务对一行rowR三列c1,c2,c3分别做了修改,那么HLog会有3个对应的日志片段,无法保证行级事务的原子性,假如RegionServer更新c2列之后发生宕机,那么一行记录中只有部分数据写入成功:
           <logseq1-for-edit1>:<keyvalue-for-edit-c1>
           <logseq2-fot-edit2>:<keyvalue-for-edit-c2>
           <logseq3-for-edit3>:<keyvalue-fot-edit-c3>
        0.94之后版本,将一个行级事务的写入操作表示为一条记录,其中WALEdit会被序列化为格式<-1, # of edits,,,>:
           <logseq#-for-entire-txn>:<WALEdit-for-entire-txn>
           <-1, 3, , , >, -1为标识符,表示新的日志结构

HLog生命周期

Hlog文件结构.jpg

个人结构图 www.processon.com/view/link/6…

MemStore
MemStore Flush产生内存条带
  • 不同Region的数据在JVM Heap中是混合存储的。
  • Region1执行flush后,该内存被释放,变成Free Space,继续为写入MemStore的数据分配空间,进而分割成更小的条带。
  • 随着MemStore中数据的不断写入并flush,整个JVM将会产生大量越来越小的内存条带,即内存碎片。
  • 随着内存碎片越来越小,最后甚至分配不出来足够大的内存给写入对象,此时会触发JVM执行Full GC合并这些内存碎片。

MemStore flush产生内存条带.jpg

个人结构图: www.processon.com/view/link/6…

MSLAB(MemStore本地分配缓存)
  • 在RegionServer JVM启动参数中加上-xx:PrintFLASStatistics=1,打印每次GC前后内存碎片的统计信息,统计维度:
  • Free Space – 老年代当前空闲的总内存容量
  • Max Chunk Size(重点) - 老年代中最大的内存碎片所占的内存容量大小
  • Num Chunks - 老年代中总的内存碎片数

MSLAB(MemStore本地分配缓存).png

个人结构图:www.processon.com/view/link/6…

HFile

HFile 逻辑结构图:www.processon.com/view/link/6…

HFile V2逻辑结构图.png

HFile V2物理结构图

4534343.png

HFile Block

78676.png

Trailer Block

45343.png

Data Block

6896767.png

Bloom Index Block

| 一次GET请求,根据布隆过滤器进行过滤查找,步骤: 1)根据待查Key在Bloom Index Block所有的索引项中根据BlockKey进行二分查找,定位对应的Bloom Index Entry; 2)根据Bloom Index Entry中BlockOffset及BlockOndiskSize加载对应的位数组; 3)对Key进行Hash映射,查看位数组相应位置是否为1,不是则表示不存在该Key,否则只是有可能存在该Key。 |

一次GET请求,根据布隆过滤器进行过滤查找,步骤:

  • 根据待查Key在Bloom Index Block所有的索引项中根据BlockKey进行二分查找,定位对应的Bloom Index Entry;
  • 根据Bloom Index Entry中BlockOffset及BlockOndiskSize加载对应的位数组;
  • 对Key进行Hash映射,查看位数组相应位置是否为1,不是则表示不存在该Key,否则只是有可能存在该Key。

7456756.png

HFile文件索引

4321.png

Block & NoneRoot Index Block

75765564.png

查看HFile元数据

# 查看HFile元数据
# hbase hfile -m -f /hbase/data/default/mytable/c606b6c02a3f6b51192988432afe38bc/colfam1/e40c31de97ff47aeb27d84a4ca59f7c8

2020-05-20 14:20:59,865 INFO  [main] hfile.CacheConfig: Created cacheConfig: 

CacheConfig:disabled
Block index size as per heapsize: 392
reader=/hbase/data/default/mytable/c606b6c02a3f6b51192988432afe38bc/colfam1/e40c31de97ff47aeb27d84a4ca59f7c8,
    compression=none,
    cacheConf=CacheConfig:disabled,
    firstKey=r/colfam1:q/1565781611071/Put,  # 在scan时可通过startkey和stopkey优化性能
    lastKey=r2/colfam1:q2/1565781667630/Put,
    avgKeyLen=22,   # 文件中所有KeyValue的平均Key长度;如果相对较小,可将Block适当调小(如32KB),避免Block内有太多KeyValue数,导致顺序扫描定位KeyValue时会增大块内延迟时间。
    avgValueLen=1,
    entries=3,
    length=4975
Trailer:
    fileinfoOffset=289,
    loadOnOpenDataOffset=181,
    dataIndexCount=1,
    metaIndexCount=0,
    totalUncomressedBytes=4884,
    entryCount=3,
    compressionCodec=NONE,
    uncompressedDataIndexSize=34,
    numDataIndexLevels=1,
    firstDataBlockOffset=0,
    lastDataBlockOffset=0,
    comparatorClassName=org.apache.hadoop.hbase.KeyValue$KeyComparator,
    encryptionKey=NONE,
    majorVersion=3,
    minorVersion=0
Fileinfo:
    BLOOM_FILTER_TYPE = ROW
    DELETE_FAMILY_COUNT = 0
    EARLIEST_PUT_TS = 1565781611071
    KEY_VALUE_VERSION = 1
    LAST_BLOOM_KEY = r2
    MAJOR_COMPACTION_KEY = false
    MAX_MEMSTORE_TS_KEY = 8
    MAX_SEQ_ID_KEY = 10
    TIMERANGE = 1565781603186....1565781667630
    hfile.AVG_KEY_LEN = 22
    hfile.AVG_VALUE_LEN = 1
    hfile.CREATE_TIME_TS = 1565785277850
    hfile.LASTKEY = r2/colfam1:q2/1565781667630/Put/vlen=0/mvcc=0
    # Hfile V3存储cell标签数据
    MAX_TAGS_LEN = 22   # 单个cell中存储标签的最大字节数
    TAGS_COMPRESSED = true    # 是否针对标签进行压缩处理 
Mid-key: \x00\x01r\x07colfam1q\x00\x00\x01l\x8F\xDBR?\x04
Bloom filter:
    BloomSize: 8
    No of Keys in bloom: 3
    Max Keys for bloom: 6
    Percentage filled: 50%
    Number of chunks: 1
    Comparator: RawBytesComparator
Delete Family Bloom filter:
    Not present
    
    

BucketCache

BucketCache内存结构

5645454.png

BucketCache标签
BucketSize大小总比Block本身大1KB,是因为Block不是固定大小,总会比64K更大一些;
默认标签:(4+1)K, (8+1)K, (16+1)K, (48+1)K, 
(56+1)K, (64+1)K, (96+1)K,…(512+1)K;
启动HBase时,系统先从小到大遍历一次所有size标签,
为每类标签分配一个Bucket,最后所有剩余的Bucket都分配最大的size标签,默认分配(512+1)K。

Bucket的size标签可以动态调整:64KB的Block比较多,
65K的Bucket用完后,其它size标签的完全空闲的Bucket可以转换成65K的Bucket,
但是会至少保留一个该size的Bucket。

BucketCache中Block缓存写入及读取流程

65454545454.png

HDFS文件检索Block

HDFS Block为128M:
HDFS主要存储大文件,当数据量大到一定程度,如果Block太小会导致Block元数据(Block所在DN位置、文件与Block之间对应关系等)非常庞大。
HDFS元数据都存储在NN上,大量元数据容易让NN成为整个集群的瓶颈。
因此,HDFS Block从最初的64M增加到128M。

HBase Block为64K:
HBase的缓存策略是缓存整个Block,如果Block设置太大会导致缓存很容易被耗尽,尤其对于很多随机读业务,设置Block太大会让缓存效率低下。

634645645.png