网上的 Hbase 调优资料参差不齐, 实在是不忍卒读, 有些都是拼凑且版本过时的东西, 我这里决定综合所有优质资源进行整合, 写一份最全, 最有深度, 不过时的技术博客. 辛苦成文, 各自珍惜, 谢谢!
1 BlockCache 唯一性
一个 RegionServer 只有一个 BlockCache.
BlockCache 的诞生就是用来优化读取性能的.
HBase Block 目前主要有 DATA,ENCODED_DATA,META,FILE_INFO,ROOT_INDEX 等.
BlockCache 目前主要有 LRUBlockCache 和 SlabCache, 以及 BucketCache 等.
2 LRUBlockCache 的三段缓存架构
hfile.block.cache.size.LRUBlockCache: 参数表示占用堆内存比例, 默认是 0.4, 目前这是唯一的 LRUBlockCache, 无法被关闭.
BlockCache 配置和 Memstore 配置的联动影响, 即: Memstore+BlockCache 的内存占比不能超过 0.8(即 80%), 否则就会报错. 注意必须留 20% 的机动空间:
hbase.regionserver.global.memstore.size+hfile.block.cache.size<=0.8
LRUBlockCache 完全基于 JVM heap 的 LRU 的方案, 当缓存写满了之后, 会根据 LRU 的算法来淘汰 block.
LRUBlockCache 的三段缓存架构:
第一段 single-access 占用 25% 的比例, 也即单次读取区, block 被读出后先放到这个区域, 当被读到多次后会升级到下一个区域.
第二段 multi-acsess 占用 50% 的比例, 也即多次读取区, 当一个被缓冲到单次读取区后又被访问多次, 会升级到这个区.
in-memory 占用 25% 的比例, 这个区域跟 Block 被访问几次没有什么关系, 它只存放那些被设置了 IN-MEMORY=true 的列族中读取出来的 block.
3 LRUBlockCache 的弊端
LRUBlockCache 完全基于 JVM Heap 的缓存, 那么势必会造成一个后果, 随着内存中对象越来越多, 每隔一段时间肯定会产生 Full GC.
4 SlabCache 堆外内存的创意(已废弃)
4.1 堆外内存烫手山芋
因为堆外内存存储的数据都是原始数据, 对于一个对象, 比如先序列化之后才能存储, 所以不能存储大对象.
堆外内存并不是 JVM 的管理范围, 所以当内存泄露的时候非常不好排查问题.
对外内存使用的是物理内存, 当使用过大的时候, 物理内存可能会爆掉.
5 BucketCache 应运而生
5.1 BucketCache 理论基础
CombinedBlockCache 是一个 LRUBlockCache 和 BucketCache 的混合体. BucketCache 是阿里贡献的. LRUBlockCache 中主要存储 Index Block 和 Bloom Block, 而将 Data Block 存储在 BucketCache 中. 因此一次随机读需要首先在 LRUBlockCache 中查到对应的 Index Block, 然后再到 BucketCache 查找对应数据块.
BucketCache 可以有三种工作模式: heap,offheap,file.heap 模式表示这些 Bucket 是从 JVM Heap 中申请, offheap 模式使用 DirectByteBuffer 技术实现堆外内存存储管理, 而 file 模式使用类似 SSD 的高速缓存文件存储数据块.
无论在哪一种工作模式下, BucketCache 都会申请许多带有固定大小标签的 Bucket, 一种 Bucket 只是一种指定的 BlockSize 的数据块, 初始化的时候申请 14 个不同大小的 Bucket, 而且即使在某一种 Bucket 空间不足的情况下, 系统也会从其他 Bucket 空间借用内存使用, 不会出现内存使用率低下的情况. 这里每个 Bucket 的大小上限为最大尺寸的 Block * 4, 比如最大容纳 Block 类型为 512KB, 那么每个 Bucket 的大小就是 512KB*4 =2018. 若配置了 4, 则会有 2048/4=512 个 4K 的空间.
我们将物理空间划分为一堆等大的 Bucket, 每一个 Bucket 有一个序号及一个 size 标签, 于是 Block 所在 bucket 的序号及其在 bucket 中的 offset 与 block 在物理空间的 offset 就形成了一一对应. 我们通过 BucketAllocator 为指定大小的 Block 寻找一个 Bucket 进行存放, 于是就得到了其在物理空间上的位置.
每个 Bucket 都有一个 size 标签, 目前对于 size 的分类, 是在启动时候就确定了, 如默认的有(8+1)K,(16+1)K,(32+1)K,(40+1)K,(48+1)K,(56+1)K,(64+1)K,(96+1)K ... (512+1)K
相同 size 标签的 Bucket 由同一个 BucketSizeInfo 管理
Bucket 的 size 标签可以动态调整, 比如 64K 的 block 数目比较多, 65K 的 bucket 被用完了以后, 其他 size 标签的完全空闲的 bucket 可以转换成为 65K 的 bucket, 但是至少保留一个该 size 的 bucket
如果最大 size 的 bucket 为 513K, 那么超过这个大小的 block 无法存储, 直接拒绝
如果某个 size 的 bucket 用完了, 那么会依照 LRU 算法触发 block 淘汰
hbase 表参数一览
- hbase(main):002:0> create 'Test',{NAME=>'d',IN_MEMORY=>'true'}
- 0 row(s) in 4.4970 seconds
- => Hbase::Table - Test
- hbase(main):003:0> describe 'Test'
- Table Test is ENABLED
- Test
- COLUMN FAMILIES DESCRIPTION
- {NAME => 'd', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'true', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS =>
- '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
- 1 row(s) in 0.2530 seconds
5.2 BucketCache 的参数设置
BucketCache 默认是开启的, 如果不想让某个列族使用 BucketCache, 可以使用一下命令
alter 'mytable' , CONFIGURATION => {CACHE_DATA_IN_L1 => 'true'}
BucketCache 相关的配置项如下:
hbase.bucketcache.ioengine : 使用的存储介质, 可选值为 heap ,offheap,file. 不设置的话, 默认为 offheap.
hbase.bucketcache.combinedcache.enabled: 是否打开组合模式(combinedcache), 默认是 true
hbase.bucketcache.size:BucketCache 所占的大小
如果设置为 0.0-1.0 , 则代表占堆内存的百分比
如果大于 1, 则代表实际的 BucketCache 的大小, 单位为 MB.
默认值为 0.0, 即关闭 BucketCache
hbase.bucketcache.bucket.sizes: 定义所有 Block 种类, 默认是 14 中, 默认值为 4,8,16,32,40......
-XX:MaxDirectMemorySize: 这是 JVM 启动参数, 该参数定义了 JVM 可以获取的堆外内存上限.
5.3 BucketCache 组合模式的强强合作
具体解释为把不同类型的 Block 分别放到 LRUCache 和 BucketCache 中, 如: Index Block 和 Bloom Block 会被放到 LRUCache 中, Data Block 被直接放进 BucketCache 中, 所以每次查询, 都会先去 LRUCache 查询一下, 然后再去 BucketCache 中查询真正的数据.
LRUCache 使用内存, BucketCache 使用 SSD,HFile 使用机械硬盘.
5.4 BucketCache 测试报告
BucketCache 自己使用内存, 碎片比较少, 所以 GC 时间大部分都要比 LRUBlockCache 短.
在缓存全部命中的情况下, LRUBlockCache 是 BucketCache 吞吐量的两倍. 在缓存基本命中的情况下, LRUBlockCache 是 BucketCache 吞吐量相当.
读写延迟, IO 方面基本相当.
强烈建议线上配置 BucketCache 模式. 可能很多专家都测试过这两种模式下的 GC, 吞吐量, 读写延迟等指标, 看到测试结果都会很疑惑, BucketCache 模式下的各项性能指标都比 LruBlockCache 差了好多, 但是突然我弄明白了, 测试肯定是在基本全内存场景下进行的, 这种情况下确实会是如此. 但是话又说回来, 在大数据场景下又有多少业务会是全内存操作呢?
5.5 RegionServer 内存分配
RegionServer 进程的内存就是 JVM 内存, 主要分为三部分: LRUBlockCache, 用于读缓存; MemStore, 用于写缓存; Other, 用于 RS 运行所必须的其他对象
6 总结
网上的 Hbase 调优资料参差不齐, 实在是不忍卒读, 有些都是拼凑且版本过时的东西, 我这里决定综合所有优质资源进行整合, 写一份最全, 最有深度, 不过时的技术博客. 辛苦成文, 各自珍惜, 谢谢
秦凯新 于深圳 201811280111
来源: https://juejin.im/post/5bfd666a6fb9a049ea38a55a