HBase-内部优化

1 WAL优化

一个Region有一个WAL实例,WAL实例启动后再内存中维护了一个ConcurrentNavigableMap,是一个线程安全的并发集合,包含了很多个WAL文件的引用,当一个WAL文件写满之后就会开始下一个文件,WAL文件数量不断增长知道达到一个阈值之后开始滚动。相关的优化参数有:

#Region中最大的WAL文件数量,默认值32(当前版本已舍弃)
hbase.regionserver.maxlogs
#HDFS块大小,没有默认值,如果不设置,则使用 hdfs的块大小
hbase.regionserver.hlog.blocksize
#WAL文件大小因子,每一个WAL文件大小通过hdfs块大小*WAL文件大小因子得出,默认0.95
hbase.regionserver.logroll.multiplier

1.1早期优化方式

早期WAL文件数量超过了maxlogs阈值就会引发WAL日志滚动,旧的日志会被清理掉,maxlogs的设置公式为:

(regionserver_heap_size * memstore fraction) /(logRollSize)
(REgionServer堆内存大小*memstore在JVM的堆内存中占的比例)/单个WAL文件的大小

WAL单个文件大小通过blocksize*影响因子。因为大多数人不知道这个公式,使用默认值导致很容易就超过32个log,造成很多不便。后来hbase将maxlogs的数值有HBase内部自己计算得出

1.2 新的优化方式

HBase内部自己的计算方式为:

Math.max(32,(regionserverHeapSize * memstoreSizeRatio *2 /logRollSize))

公式与之前的相差不大,只是分母多了个2倍


2 BlockCache的优化

一个RegionServer只有一个BlockCache,BlockCache不是存储的必要部分,只是用来优化读取性能的,读取数据的时候,client会先从zk获取Region信息,然后到RegionServer上查询BlockCache是否有缓存数据,之后采取memstore和Region中查询,如果获取到了数据,返回同同事会将数据缓存一份到BlockCache。
BlockCache默认是开启的,可以通过如下方式来关闭

alter 'mytable',CONFIGURATION =>{NAME =>'myCF',BLOCKCACHE => 'false'}

BlockCache的实现方案有四种:

2.1 LRUBlockCache

0.92版本之前只有该方法,Least Recently Used,近期最少使用算法的缩写。模仿分代垃圾回收算法,LRU分为三个区域:

  • single-access:单次读取区,占比25%,block读出后先放到该区域,当被读取多次后升级到下一个区域
  • multi-access:多次读取区,占比50%,当一个被缓存到单次读取区又被多次访问,就会被升级到该区域
  • in-memroy:放置设置为IN-MEMORY=true的列族读取出来的block,占比25%。对应列族Block一开始就被放到了in-memory中,不经过单次读取和多次读取区域,并且具有最高存活时间,淘汰Block时候,这个区域Block最后被考虑到。

BlockCache无法关闭,只能调整大小,参数为:

hfile.block.cache.size LRUBlockCache:占用堆内存的比例,默认0.4

配置需要注意的是,Memstor+BlockCache占用的堆内存总量不能超过0.8,否则会报错,留下20%作为机动空间,而Memstore的默认内存也是0.4,所以如果往大调整任何一个值,都必须调小另一个值。
BlockCache有很多优势,通过内存做缓存调高读取性能,但是BlockCache完全基于JVMHeap会导致随着内存中的对象越来越多,每隔一段时间都会引发FULLGC。而为了避免FullGC,出现了基于堆外内存的BlockCache方案。

2.2 SlabCache(已废弃)

所谓堆外内存(off-heap memory),就是不属于JVM管理的内存范围,也就是隶属于原始内存的区域,堆外内存大小可以通过如下设置:

-XX:MaxDirectMemsorySize=60M

堆外内存最大的好处就是JVM几乎不会停顿,也不用害怕回收时候业务卡住,但是堆外内存有几点比较大的缺点:

  • 堆外内存存储的数据都是原始数据,如果是一个对象,比如序列化之后才能存储,所以不能存储太大太复杂的对象
  • 堆外内存并不是在JVM的管理范围,所以当内存泄漏的时候很不好排查问题
  • 堆外内存由于用的是系统内存,使用太多可能导致物理内存溢出,或者因为开启了虚拟内存导致了和磁盘的直接交互

SlabCache调用了nio的DirectByteBuffers,按照8:2的比例划分为两个区域:

  • 80%:存放约等于一个BlockSize默认值的Block(64k)
  • 20%:存放约等于两个BlockSize默认值的Block(128K)

所以如果数据块大于128K将不会被放入ScabCache中,同时SlabCache也是用LRU算法对缓存对象进行淘汰。所以有时候自定义了BlockSize后,会导致SlabCache放不进去数据,所以HBase使用了LRU和Slab组合的方式:

  • 当一个Block被取出的时候同时放到SlabCache和LRUCache中
  • 读请求的时候,先查看LRUCache,如果没有采取SlabCache中,如果查到了,就把Block放到LRUCache中。

感觉ScabCache就是LRUCachbe的二级缓存,所以管这个方案中的LRUCache为L1Cache,SlabCache为L2Cache。但实际上SlabCache对blockSize值定的太死,大部分请求还是直接走了LRU,所以对FullGC改善不明显,后来被废弃了。

2.3 BucketCache(同样适用堆外内存)

BucketCache是阿里工程师设计的,借鉴了SlabCache,并且有了改善,

2.3.1 BucketCache的特点

  • BucketCache上来直接分配了14种区域,注意是14种,大小分别是4、8、16、32、40、48、56、64、96、128、192、256、384、512KB的block,且种类列表可以通过hbase.bucketcache.bucket.sizes属性来定义(种类之间用逗号分隔,不一定是14个)
  • BucketCache存储不一定使用堆外内存,可以使用堆(heap)、堆外(offheap)、文件(file)。可以通过hbase.bucketcache.ioengine为上述三个单词中的一个做配置
  • 每个Bucket的大小上限为block4,比如设置最大的Block类型为512KB,那么每个Bucket最大为512KB4=2018KB
  • 要保证每一个block类型至少有一个Bucket的空间,否则直接报错,按照每个Bucket的大小上限均分为多个Bucket,之前的例子至少要保证2048KB*14这么大的存储空间

BucketCache可以自己划分内存空间,自己管理内存空间,Block放进去的时候会考虑offset偏移量,所以内存碎片少,发生GC时间短。
另外,之所以会使用file作为存储介质,是因为SSD硬盘的使用,极大地改进了SlabCache使用率低的问题。

2.3.2 BucketCache的相关配置

BucketCache默认是开启的,可以使用如下配置关闭某个列族的BucketCache

hbase > alter 'table_name' CONFIGURATION => {C1 => 'true'}

意思是只使用一级缓存(LRUCache),不使用二级缓存。
BucketCache相关配置有:

#使用的存储介质,可以为heap/offheap/file,默认offheap
hbase.bucketcache.ioengine
#是否打开组合模式,默认为true
hbase.bucketcache.combinedcache.enabled
#BucketCache所占大小,0.0-1.0代表站堆内存比例,大于1的值表示MB为单位的内存;默认为0.0,即关闭BucketCache
hbase.bucketcache.size
#定义所有的Block种类,单位为B,每一种类型必须是1024的整数倍
hbase.bucketcache.bucket.sizes
#该值不是在hbase-site.xml中配置,而是一个启动参数,默认按需获取堆外内存,如果配置了,就相当于设置了堆外内存上限
-XX:MaxDirectMemorySize

2.4 组合模式

在实际生产情况下,虽然BucketCache有很多好处,但是LRUCache性能远比BucketCache强,因为这些二级Cache从速度和可管理性上始终无法和完全基于内存的LRUCache相比。BucketCache和LRU也并不是简简单单的一二级缓存集合,而是称为组合模式,把不同类型的数据分别放到不同的存储策略中。
Index Block会放到LRUCache中,Data Block放到了BucketCache中,所以查询请求在BlockCache阶段会先查询一下LRUCache,会先到LRU查询,然后到BucketCache中查询出真正的数据。数据从一级缓存到二级缓存最后到硬盘,数据从小到大,存储介质由快到慢,比较符合成本和性能规划,而比较合理的存储介质使用是:LRU用内存->BucketCache用SSD->HFile使用磁盘。


3 Memstore的优化

首先大家要明白,Memstore虽然是将数据存储在内存中,但并不是为了加快读取速度,而是为了维持数据结构。HDFS文件不支持修改,为了维持HBase中的数据是按照rowkey顺序来存储的,所以使用Memstore先对数据进行整理,之后才持久化到HDFS上。

3.1 Memstore的刷写机制

Memstore的数据刷写又叫flush,有5种情况会触发刷写。

3.1.1 大小达到了刷写阈值

当单个Memstore占用的内存大小达到了hbase.hregion.memstore.flush.size的配置大小后就回触发一次刷写,默认128MB,生成一个HFile文件,因为刷写是定期检查的,所以无法及时的在数据到达阈值的一瞬间就触发刷写,有时候数据写入非常快,会导致在检查时间还没到之前,Memstore的数据量就达到了刷写阈值的好几倍,就会导致触发写入阻塞机制,此时数据无法写入Memstore,只能进行刷写,影响比较严重。而阻塞机制有个阀值,定义如下:

hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier
hbase.hregion.memstore.flush.size是刷写阀值,默认134217728=128MB
hbase.hregion.memstore.block.multiplier 是一个倍数,默认为4

也就是如果达到了刷写阀值的4倍就会导致刷写阻塞,会阻塞所有写入该Store的写请求,主要是为了应对数据如果继续急速增长导致的更加严重的后果。在解决刷写阻塞的时,并不能一味调大阻塞阀值,而是要综合考虑HFile的相关参数设置。

3.1.2 RegionServer内部所有Memstore的总和达到了阀值

具体的阀值计算公式为:

globalMemStoreLimitLowMarkPercent* globalMemStoreSize
globalMemStoreLimitLowMarkPercent是全局memstore刷写下限,是一个百分比,范围0.0-1.0,默认0.95
globalMemStoreSize是全局memstore的容量,其计算方式为RegionServer的堆内存大小*hbase.regionserver.global.memstore.size,默认0.4,也就是堆内存的40%

总体来说,如果全局Memstore的总和达到了分配给Memstore最大内存的95%,就会导致全局刷写,默认有40%的内存会分给Memstore。而当超过了Memstore的最大内存,也就是堆内存的40%就会触发刷写阻塞。如果有16G堆内存,默认情况下:

#达到该值会触发刷写
16*0.4*0.95=0.608
#达到该值会触发刷写阻塞
16*0.4=6.4

3.1.3 WAL的数量大于maxLogs

当WAL的数量大于maxLogs的时候,也会触发一次刷写,会报警一下,该操作一定不会导致刷写阻塞。
maxLogs的计算公式为:

Math.max(32,(regionserverHeapSize * memstoreSizeRatio * 2 / logRollSize))

该操作主要为了腾出内存空间。

3.1.4 Memstore达到刷写时间间隔

默认时间为1小时,如果设置为0则关闭自动定时刷写。配置参数为:

 hbase.regionserver.optionalcacheflushinterval 默认为3600000,1个小时

3.1.5 手动触发刷写

JavaAPI Admin接口提供的方法为:

#对单个表刷写
flush(TableName tableName)
#对单个Region刷写
flushRegion(byte[] regionName)

hbase shell命令:

#单表刷写
flush 'tableName'
#单个Region刷写
flush 'regionname'

3.2 总结

memStore的优化可以主要关注放置触发阻塞机制了,要合理设置Memstore的内存,以及RegionServer的堆内存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • HBase那些事 @(大数据工程学院)[HBase, Hadoop, 优化, HadoopChen, hbase]...
    分痴阅读 3,925评论 3 17
  • 本文首先简单介绍了HBase,然后重点讲述了HBase的高并发和实时处理数据 、HBase数据模型、HBase物理...
    达微阅读 2,724评论 1 13
  • HBase存储架构图 HBase Master 为Region server分配region 负责Region s...
    kimibob阅读 5,561评论 0 52
  • 参考:https://www.jianshu.com/p/569106a3008f 最近在逐步跟进Hbase的相关...
    博弈史密斯阅读 845评论 1 1
  • Hbase架构与原理 HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang所撰写的Goo...
    全能程序猿阅读 86,278评论 2 37