HBase

HBase

基于Solr创建HBase二级索引,数据一致性问题有什么解决方法吗

Solrhadoop 回复了问题 • 2 人关注 • 1 个回复 • 245 次浏览 • 2019-09-04 10:29 • 来自相关话题

java.io.IOException: Added a key not lexically larger

回复

hbasekite 发起了问题 • 1 人关注 • 0 个回复 • 112 次浏览 • 2019-07-18 11:27 • 来自相关话题

phoenix使用hbase命令激活异步建立的二级索引

Phoenixxbyang18 回复了问题 • 5 人关注 • 6 个回复 • 2400 次浏览 • 2019-07-17 09:08 • 来自相关话题

加入群一年多转发你们自己群主发的东西踢我?

回复

hbasejiatianyao 发起了问题 • 1 人关注 • 0 个回复 • 196 次浏览 • 2019-07-13 08:31 • 来自相关话题

waiting for 51 actions to finish on table: xxxx

hbasecodermonkey 回复了问题 • 4 人关注 • 2 个回复 • 1718 次浏览 • 2019-07-11 19:56 • 来自相关话题

pom引入hbase2.0.2依赖问题

回复

hbasechb 发起了问题 • 1 人关注 • 0 个回复 • 201 次浏览 • 2019-06-12 22:38 • 来自相关话题

spark连接Phoenix报错

Phoenixyangchenggang 回复了问题 • 4 人关注 • 7 个回复 • 1878 次浏览 • 2019-05-11 10:57 • 来自相关话题

hbase执行手动大合并的时候,执行了一周还是没有结束,是怎么回事?

回复

hbase果农 发起了问题 • 1 人关注 • 0 个回复 • 252 次浏览 • 2019-04-22 08:50 • 来自相关话题

hbase 奇怪的metric错误

hbasegp1314 回复了问题 • 3 人关注 • 2 个回复 • 843 次浏览 • 2019-04-01 13:09 • 来自相关话题

HBase coprocessor介绍及使用

hbase过往记忆 发表了文章 • 0 个评论 • 455 次浏览 • 2019-03-28 10:52 • 来自相关话题

讲师:陈杨——快手大数据高级研发工程师
毕业于浙江大学,现负责快手HBase的维护与研发,支持视频、特征、用户画像、IM等海量数据的存储;
一直致力于大数据基础架构和hadoop生态的学习与研发;同时在hbase与hdfs的基础上,和团队一起研发了大数据存储与分析相关系统,如blobstore、bitbase等。

内容概要:

(1)讲解hbase coprocessor的原理以及使用场景,
(2) coprocessor整个流程实战,包括开发,加载,运行以及管理
(3)结合1,2分析coprocessor在rsgroup中的具体使用 查看全部
讲师:陈杨——快手大数据高级研发工程师
毕业于浙江大学,现负责快手HBase的维护与研发,支持视频、特征、用户画像、IM等海量数据的存储;
一直致力于大数据基础架构和hadoop生态的学习与研发;同时在hbase与hdfs的基础上,和团队一起研发了大数据存储与分析相关系统,如blobstore、bitbase等。

内容概要:

(1)讲解hbase coprocessor的原理以及使用场景,
(2) coprocessor整个流程实战,包括开发,加载,运行以及管理
(3)结合1,2分析coprocessor在rsgroup中的具体使用

hbase优化之旅(四)-regionserver从17优化到10台的小结

hbasechuxiao 发表了文章 • 3 个评论 • 623 次浏览 • 2019-03-25 18:47 • 来自相关话题

本文是前几篇文章的小结,介绍如何通过技术手段,挖掘hbase服务的潜力,将分组的服务器数从17台降低到10台。
首发于专栏 :https://zhuanlan.zhihu.com/p/60357239 

目录
确定优化目标
系统资源瓶颈和优化效果
cpu利用率
内存使用
网络IO
磁盘IO
存储量
稳定性
综述
优化细节
合并region
hbase参数调整
未来的工作
近期工作
中远期工作

确定优化目标
沟通交流后,业务方更看重降低成本。数据量梳理后略有降低,保证吞吐,无长期请求堆积前提下可以放宽延时要求。为了更快的进行优化,放宽稳定性可以要求接受短期波动。
另外,该分组的regionserver之前存在不稳定的问题,这次优化也一并解决。

系统资源瓶颈和优化效果
降低成本,即省机器,就是用更少的服务器满足业务需求。需要搞清楚单台服务器的瓶颈在哪。下面分析单台服务器的指标和优化效果。
cpu利用率
因为hbase是重IO,轻计算的服务,一般来说hbase服务cpu利用率较低。优化前单台cpu利用率平均在5%,峰值在10%左右。直接机器减半也没有压力。
空闲的cpu利用率,可以用来置换更高的存储压缩比,节约存储空间。更多的业务线程和gc线程,提高吞吐能力,减少gc对系统吞吐和延时毛刺的影响。
峰值cpu在40%以下,说明cpu资源充足富裕,可以减机器,用cpu换存储空间,提高业务吞吐。


内存使用
256GB内存,datanode 4g,regionserver堆内50g,堆外100g,空闲大概100g。可以提高单机regionserver内存到堆内100g,堆外120g,留10%的空闲。
提高内存的好处很多。单机负载不变的前提下,增加堆内内存,可以降低gc频率减少毛刺出现频率,降低gc时间占比提高吞吐(另外如果mixed gc周期能超过大多数region flush的周期,回收会更有效率)。增加memstore写缓存,可以降低写放大效应,从而降低网络和磁盘IO。降低写放大,还可以减少compact,提高缓存有效性从而增加缓存命中率,进一步降低读IO,减少读请求延时。增加堆外内存,可以缓存命中率。
举个例子,一台服务器提高内存使用量,同时region数变为其他服务器的2倍。堆外内存增加20%而region数翻倍,单纯数字上来看缓存命令率应该下降,实际稳定后命中率保持在96%高于其他服务器的94%,这就是降低compact提高命中率的收益。
增大内存的坏处是,单次gc时间会更长,极端情况下请求延时的毛刺会更明显。另外一些hbase内部的数据结构,如memstore的ConcurrentSkipListMap,对象数太多性能会下降的很厉害,需要结合用户需求,在region数,hbase-site参数,gc参数上综合考虑,做一个权衡。


网络IO
网络In 20MB,Out 40到100MB波动。万兆网卡峰值也就百MB以内,机器数砍半没压力。

磁盘IO
非major compact时段,磁盘读流量较高,高峰期200MB到300MB,主要来自compact。写流量50MB/s。磁盘流量较高是个瓶颈,需要技术优化。
磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5左右),如果不考虑服务端filter过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
用户的业务场景没用filter,所以可以推论读IO主要来自compact ,验证了写放大效应明显。
经过参数优化降低写放大效应,region数不变,单机磁盘读IO下降到之前的1/3,80MB左右。
又经过region合并,参数继续优化,单机两倍region负载,读Io保持在150MB到200MB之间,比之前单倍region负载还要低很多。

存储量
集群存储峰值大概有三个时间点,每周major compact前,每周major compact后,major compact中(大概是major compact前存储*(1+并发major region数/总region数))。
目前没有明确用哪个存储量衡量峰值,一般定期检查,发现用到差不多了就和用户沟通加机器。不允许独立分组存储总量大于分组机器所提供的存储总量。
在major compact前取了数据的总量,刚好跟10台的存储量差不多,可以降低到10台。

稳定性
如系列文章上一篇gc执行细节和参数调优方法论详所说,分组之前存在稳定性的问题。出现问题时需要人工操作迁移region,重启。当机器数减到17台,直接减机器立刻加剧了问题出现的频率。经分析,问题主要出在gc参数上,详见上一篇文章。

综述
通过瓶颈分析,解决了磁盘IO问题后,10台服务器是存储量瓶颈,所以第一步的目标是降到10台。



优化细节
合并region
之前分组17台,单机region数大概500上下,region数太多导致flush都是小文件,写放大罪魁祸首。合并小于10g 的region,将region数降低到单机130+,写放大效应立刻降低了,可以减少机器数了。
一个批量写请求,regionserver内写多个region是串行的,这个角度减少region数可以改善写延时。同时单个region memstore太大,ConcurrentSkipListMap的结构插入性能会降低。目前没发现减少region后明显的写延时差别。杭州的同事分享过阿里对memstore数据结构的优化,版本升级到1.4.8后可以评估能否用的到。
另外,大region会导致major compact压力更大。这个可以通过修改compact policy来解决。

hbase参数调整
增大内存
堆内存 50g->100g
堆外内存 100g->120g
大内存降低写放大效应,提高读缓存命中率,支持更高的吞吐能力。改善了gc表现。


基础配置
hbase.regionserver.handler.count 192->384
hbase.ipc.server.callqueue.read.share 无->0.4

hbase.ipc.server.callqueue.handler.factor 无->0.2
两倍region后,监控看当前handler经常达到192上限,按其他公司经验调大一倍。有时能打到上限。
线程多可以减少队列等待时间,可能增加请求处理时间,监控看,调大能显著减少队列等待时间p99,请求时间p99没有明显变化。
读写分离主要是能降低p99,避免慢写入/高并发scan等耗时长的操作堵塞句柄。192个句柄时,等待队列的p99有时会持续在500ms-1s左右。调大到384,增加读写分离,等待队列p99就维持在100ms以下了。
5个句柄使用一个队列,是为了降低高并发时的锁资源争抢。需要权衡队列比例,如果1,2个句柄就使用一个队列,很容易几个慢请求就把队列里的后续请求都堵住了。目前设置为5:1.

memstrore
hbase.hstore.flusher.count 15-->8 flush线程数
base.regionserver.optionalcacheflushinterval 无->7200000 (1小时到2小时)
hbase.regionserver.hlog.blocksize 无 -> 268435456 (实际值没变,由使用hdfs块大小变为显式设置)
hbase.regionserver.maxlogs 52 -> 200

除了regionserver重启时memstore flush外,有几种情况会触发memstore flush,分别是单个region的memstore达到上限,单个region的memstore距离上次flush过了刷新周期,hlogs达到上限flush涉及的还未flush的region,总memstore达到内存设置上限。这几种情况按照从好到差顺序排列,越靠后对系统的稳定性影响越高,应尽量避免。

堆内存的增加,直接增大了memstore内存上限,增大了单region flush的容量,可以刷大文件减少写放大效应。同时尽量让region写满128MB再flush,可以错开flush时间,错开compact时间,降低磁盘IO峰值,减少flush和compact排队现象。
10台均分region,调整后单台的memstore在10g到30g波动。为了尽量128MB在刷,其他几种flush方式的参数要跟着调整。

虽然我们要避免后面几种flush情况出现,但当业务突然有写入热点,或机器重启各region的memstore重置,可能会触发定时刷新/达到hlogs上限引起集中flush。为降低同时flush并发高引起的问题,降低了并发flush数。由于memstore足够大,单次flush周期长,即使控制flush并发,也不会充暴memstore内存造成写堵塞。
此外,控制flush数可以间接控制minor compact的压力。

按10台regionserver计算规模和请求量,白天绝大多数region在2个内memstore达到128MB,2小时刷新可以确保绝大多数region写满128MB自动刷新,减少写放大。如果按默认1小时,重启后的前2,3天里,会有很多region在相同时间flush,触发compact,系统压力很大。2,3天后,靠着刷新时间每次的随机波动才能慢慢分散开。所以应确保白天峰值写入量时,大多数region都能在刷新周期里写满flush。

hlogs过少会造成刷新周期太短。以之前经常延时变长的一台服务器为例,平均5到10分钟强刷一次,刷的都是小文件,带来了严重的读写放大后果。之前region数三倍于现在,和gc参数不合适一起造成了偶发的gc时间占比高影响业务的问题。 另外,目前hlogs 50的配置会造成同时flush大量region,同时compact,系统压力大,造成请求和吞吐的毛刺。
maxlogs的配置多大合适?maxlogs 从90,到120,150,200,250,300,350,400都实验过,越大的hlogs对缓解region写入不均,调大flush尺寸和周期越有利 。
单纯按照流传的公式来看, 下限 50 * 1024/(256*0.95)= 210,上限60 * 1024/(256*0.95)= 252,应该在210到252之间。实际由于各region会陆续触发flush,hlogs即使到达252,memstore总内存可能只有10-20g,依然可以调大。
hlogs上限提高能解决重启后同时flush密集的问题。重启后,各region memstore都是从0开始缓存,hlogs到上限时如果大量region没写满,会触发大量region同时 flush,这些region的memstore再次清零了,下个周期依然有大量region同时flush,打散速度慢于刷新周期触发的flush。hlogs上限出发的同时flush,compact对gc压力很大,请求延时会周期性显著提升。如果256MB flush,8台服务器,hlogs需要到400。128MB则200即可。
hlogs过多可能有什么影响?1.重启时间变长。重试时间长本质是总memstore增大,flush时间长造成的。之前17台规模单台重启大概1分钟,8台规模单台大概1分50秒,总时间并没有增加。再就是如果某台regionserver挂掉,集群要重读未flush的hlogs,hlogs多了会增加重读的量,增加集群负担。

综上所述,最终flush size定为128MB,hlogs上限定为200。

读cache
hfile.block.cache.size 0.19 ->0.2
hbase.bucketcache.size 102400 -> 132000
hbase.bucketcache.percentage.in.combinedcache 0.9 -> 0.85

缓存大小配合堆内堆外内存增加的调整,缓存变成堆外上限112g,堆内20g。L1和L2的比例按实际线上情况由0.9调整为0.85.


compact
hbase.regionserver.thread.compaction.small 6 -> 8
hbase.regionserver.thread.compaction.large 3 -> 4

略微提高minor compact 和major compact速度,尤其是major compact速度,以便机器减少到一半时,夜里能major完。调到12,gc压力过大,所以只是微调。

hdfs
dfs.client.hedged.read.threadpool.size 50 ->300 
dfs.client.hedged.read.threshold.millis 150->500
hbase.regionserver.hlog.slowsync.ms 无->400
compact负载一高,200线程池会报大量的线程池满,资源不够用,所以调到300。
我们用多路读是为了当磁盘故障时,可以读其他副本。如果超时时间太低,可以读本地的去读了远程副本,显著增大集群网络和磁盘IO。读包括compact的读,是轻延时重吞吐的,集群磁盘IO负载高,延时增加,触发多路读又增大了集群的IO压力。尤其是本地化不是100%时,会读其他机器上的副本,400毫秒也依然容易超时,所以超时时间改为500,确保在一般的高负载和非本地化场景中,也不会给集群额外的压力,只有磁盘真正故障堵塞读的时候再读其他副本上。由于95%以上的读都来自内存缓存,500毫秒的最大超时时间并不会造成显著的读请求延时升高,毕竟常态的gc也要几百ms时间。
负载稍微高点,日志文件满屏都是wal log slow,淹没了其他需要关注的问题。由于wal是单线程顺序写单文件,写入速度是有瓶颈的,调到400,只有负载较高时才会打印。

gc参数调整
请看上一篇,本文不再叙述。

未来的工作
近期工作
打散major compact执行时间
现在集群内所有业务分组同一天晚上进行major compact ,集群网络和磁盘IO压力大,存储量也会同时飙升。major compact即将打散到每周7天的晚上,以降低集群的压力。

换compact policy
compact policy优化,需要用户配合做客户端代码优化,和业务方暂定4月初共建。
现在所有业务用的都是默认的default policy,中规中矩。而该项目业务场景非常适合用Date Tiered Compaction,该策略能极大降低compact写放大效应尤其是major compact的压力,并且提升近期热点数据查询速度。
更换该策略,需要业务方略微修改客户端代码,读写时增加TTL的设置,否则可能会降低查询速度。

换压缩算法
换高压缩比算法gz能进一步减少存储总量,已有经验是可以降低30%左右。即存储量视角,可以降到7台服务器规模。换压缩算法会增加cpu利用率,可能对用户读写造成未知的影响,待4月和用户共建。


中远期工作
2副本
好处是存储量立刻降低1/3,坏处是集群同时坏2块盘的概率比坏3块高得多,更容易丢数据。一般是偏离线,稳定性要求不高,存储量偏高的业务,数据在hive有一份,即使有数据缺失可以很快从hive导一份。需要和用户进一步确定业务模式是否能采用。

超卖
目前成本分摊策略,独立业务分组按regionserver服务器数分摊成本,如果独立业务分组存储总量大于分组机器所提供的存储总量,即认为存储量达到瓶颈。就必须要增加机器,不同的业务瓶颈不一样,按存储量严格限制,不利于提升集群资源的整体利用率。
所以从整个集群的资源利用率来看,允许部分吞吐,延时要求不高的业务使用更多的存储,即存储超卖,可以更有效提高集群资源利用率,释放数据引擎潜力,降低业务方使用成本,为公司省钱。
超卖涉及三个维度。超卖存储每TB价格,超卖存储量如何计算。超卖比例,
现在每TB成本是整机打包折算的,而超卖的存储只涉及纯磁盘的折算成本。要超卖存储,需要额外提供超卖存储的每TB价格,这个价格应该比整机的每TB成本低一些。
超卖存储量可以用定时程序收集分组的hdfs存储量,保留最高值,除0.9作为该月实际存储用量,减去分组机器数提供的存储就是超卖量。
超卖比例,前期可以先允许超卖分组存储的20%,并监控集群整体磁盘利用率情况。超卖试运行一段时间后,如果集群存储依然空闲,可以尝试提高超卖比例上限。
目前成本分摊的方式,只支持业务分组按整机打包折算,没有超卖的分摊方式。待评估实际需求后,再来决定是否允许超卖。
如果有业务方需要过高的超卖比例,不适用于现在的集群架构,需要考虑ssd和sata混部的架构。

ssd和sata混部
目前业务按数据量和期望延时的不同分了两个集群,1个是ssd集群,一个是sata盘集群。ssd读写更快,但成本高,存储量有限。
业内最顶尖的做法,是允许ssd和sata盘混部,好处有2。一个是3副本一个是ssd2个是sata盘,保证性能的同时降低存储成本。另一个是时间相关数据按时间分块存储在不同介质上,例如支付宝账单,3个月内ssd,1年内sata盘,一年前的历史数据就存储在更便宜的存储介质上了。历史类数据都适合用这种方式。
混部的方式,在软件,集群架构,机器采购方面都有颇多挑战,是个长期的工作。 查看全部
本文是前几篇文章的小结,介绍如何通过技术手段,挖掘hbase服务的潜力,将分组的服务器数从17台降低到10台。
首发于专栏 :https://zhuanlan.zhihu.com/p/60357239 

目录
确定优化目标
系统资源瓶颈和优化效果
cpu利用率
内存使用
网络IO
磁盘IO
存储量
稳定性
综述
优化细节
合并region
hbase参数调整
未来的工作
近期工作
中远期工作

确定优化目标
沟通交流后,业务方更看重降低成本。数据量梳理后略有降低,保证吞吐,无长期请求堆积前提下可以放宽延时要求。为了更快的进行优化,放宽稳定性可以要求接受短期波动。
另外,该分组的regionserver之前存在不稳定的问题,这次优化也一并解决。

系统资源瓶颈和优化效果
降低成本,即省机器,就是用更少的服务器满足业务需求。需要搞清楚单台服务器的瓶颈在哪。下面分析单台服务器的指标和优化效果。
cpu利用率
因为hbase是重IO,轻计算的服务,一般来说hbase服务cpu利用率较低。优化前单台cpu利用率平均在5%,峰值在10%左右。直接机器减半也没有压力。
空闲的cpu利用率,可以用来置换更高的存储压缩比,节约存储空间。更多的业务线程和gc线程,提高吞吐能力,减少gc对系统吞吐和延时毛刺的影响。
峰值cpu在40%以下,说明cpu资源充足富裕,可以减机器,用cpu换存储空间,提高业务吞吐。


内存使用
256GB内存,datanode 4g,regionserver堆内50g,堆外100g,空闲大概100g。可以提高单机regionserver内存到堆内100g,堆外120g,留10%的空闲。
提高内存的好处很多。单机负载不变的前提下,增加堆内内存,可以降低gc频率减少毛刺出现频率,降低gc时间占比提高吞吐(另外如果mixed gc周期能超过大多数region flush的周期,回收会更有效率)。增加memstore写缓存,可以降低写放大效应,从而降低网络和磁盘IO。降低写放大,还可以减少compact,提高缓存有效性从而增加缓存命中率,进一步降低读IO,减少读请求延时。增加堆外内存,可以缓存命中率。
举个例子,一台服务器提高内存使用量,同时region数变为其他服务器的2倍。堆外内存增加20%而region数翻倍,单纯数字上来看缓存命令率应该下降,实际稳定后命中率保持在96%高于其他服务器的94%,这就是降低compact提高命中率的收益。
增大内存的坏处是,单次gc时间会更长,极端情况下请求延时的毛刺会更明显。另外一些hbase内部的数据结构,如memstore的ConcurrentSkipListMap,对象数太多性能会下降的很厉害,需要结合用户需求,在region数,hbase-site参数,gc参数上综合考虑,做一个权衡。


网络IO
网络In 20MB,Out 40到100MB波动。万兆网卡峰值也就百MB以内,机器数砍半没压力。

磁盘IO
非major compact时段,磁盘读流量较高,高峰期200MB到300MB,主要来自compact。写流量50MB/s。磁盘流量较高是个瓶颈,需要技术优化。
磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5左右),如果不考虑服务端filter过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
用户的业务场景没用filter,所以可以推论读IO主要来自compact ,验证了写放大效应明显。
经过参数优化降低写放大效应,region数不变,单机磁盘读IO下降到之前的1/3,80MB左右。
又经过region合并,参数继续优化,单机两倍region负载,读Io保持在150MB到200MB之间,比之前单倍region负载还要低很多。

存储量
集群存储峰值大概有三个时间点,每周major compact前,每周major compact后,major compact中(大概是major compact前存储*(1+并发major region数/总region数))。
目前没有明确用哪个存储量衡量峰值,一般定期检查,发现用到差不多了就和用户沟通加机器。不允许独立分组存储总量大于分组机器所提供的存储总量。
在major compact前取了数据的总量,刚好跟10台的存储量差不多,可以降低到10台。

稳定性
如系列文章上一篇gc执行细节和参数调优方法论详所说,分组之前存在稳定性的问题。出现问题时需要人工操作迁移region,重启。当机器数减到17台,直接减机器立刻加剧了问题出现的频率。经分析,问题主要出在gc参数上,详见上一篇文章。

综述
通过瓶颈分析,解决了磁盘IO问题后,10台服务器是存储量瓶颈,所以第一步的目标是降到10台。



优化细节
合并region

之前分组17台,单机region数大概500上下,region数太多导致flush都是小文件,写放大罪魁祸首。合并小于10g 的region,将region数降低到单机130+,写放大效应立刻降低了,可以减少机器数了。
一个批量写请求,regionserver内写多个region是串行的,这个角度减少region数可以改善写延时。同时单个region memstore太大,ConcurrentSkipListMap的结构插入性能会降低。目前没发现减少region后明显的写延时差别。杭州的同事分享过阿里对memstore数据结构的优化,版本升级到1.4.8后可以评估能否用的到。
另外,大region会导致major compact压力更大。这个可以通过修改compact policy来解决。

hbase参数调整
增大内存

堆内存 50g->100g
堆外内存 100g->120g
大内存降低写放大效应,提高读缓存命中率,支持更高的吞吐能力。改善了gc表现。


基础配置
hbase.regionserver.handler.count 192->384
hbase.ipc.server.callqueue.read.share 无->0.4

hbase.ipc.server.callqueue.handler.factor 无->0.2
两倍region后,监控看当前handler经常达到192上限,按其他公司经验调大一倍。有时能打到上限。
线程多可以减少队列等待时间,可能增加请求处理时间,监控看,调大能显著减少队列等待时间p99,请求时间p99没有明显变化。
读写分离主要是能降低p99,避免慢写入/高并发scan等耗时长的操作堵塞句柄。192个句柄时,等待队列的p99有时会持续在500ms-1s左右。调大到384,增加读写分离,等待队列p99就维持在100ms以下了。
5个句柄使用一个队列,是为了降低高并发时的锁资源争抢。需要权衡队列比例,如果1,2个句柄就使用一个队列,很容易几个慢请求就把队列里的后续请求都堵住了。目前设置为5:1.

memstrore
hbase.hstore.flusher.count 15-->8 flush线程数
base.regionserver.optionalcacheflushinterval 无->7200000 (1小时到2小时)
hbase.regionserver.hlog.blocksize 无 -> 268435456 (实际值没变,由使用hdfs块大小变为显式设置)
hbase.regionserver.maxlogs 52 -> 200

除了regionserver重启时memstore flush外,有几种情况会触发memstore flush,分别是单个region的memstore达到上限,单个region的memstore距离上次flush过了刷新周期,hlogs达到上限flush涉及的还未flush的region,总memstore达到内存设置上限。这几种情况按照从好到差顺序排列,越靠后对系统的稳定性影响越高,应尽量避免。

堆内存的增加,直接增大了memstore内存上限,增大了单region flush的容量,可以刷大文件减少写放大效应。同时尽量让region写满128MB再flush,可以错开flush时间,错开compact时间,降低磁盘IO峰值,减少flush和compact排队现象。
10台均分region,调整后单台的memstore在10g到30g波动。为了尽量128MB在刷,其他几种flush方式的参数要跟着调整。

虽然我们要避免后面几种flush情况出现,但当业务突然有写入热点,或机器重启各region的memstore重置,可能会触发定时刷新/达到hlogs上限引起集中flush。为降低同时flush并发高引起的问题,降低了并发flush数。由于memstore足够大,单次flush周期长,即使控制flush并发,也不会充暴memstore内存造成写堵塞。
此外,控制flush数可以间接控制minor compact的压力。

按10台regionserver计算规模和请求量,白天绝大多数region在2个内memstore达到128MB,2小时刷新可以确保绝大多数region写满128MB自动刷新,减少写放大。如果按默认1小时,重启后的前2,3天里,会有很多region在相同时间flush,触发compact,系统压力很大。2,3天后,靠着刷新时间每次的随机波动才能慢慢分散开。所以应确保白天峰值写入量时,大多数region都能在刷新周期里写满flush。

hlogs过少会造成刷新周期太短。以之前经常延时变长的一台服务器为例,平均5到10分钟强刷一次,刷的都是小文件,带来了严重的读写放大后果。之前region数三倍于现在,和gc参数不合适一起造成了偶发的gc时间占比高影响业务的问题。 另外,目前hlogs 50的配置会造成同时flush大量region,同时compact,系统压力大,造成请求和吞吐的毛刺。
maxlogs的配置多大合适?maxlogs 从90,到120,150,200,250,300,350,400都实验过,越大的hlogs对缓解region写入不均,调大flush尺寸和周期越有利 。
单纯按照流传的公式来看, 下限 50 * 1024/(256*0.95)= 210,上限60 * 1024/(256*0.95)= 252,应该在210到252之间。实际由于各region会陆续触发flush,hlogs即使到达252,memstore总内存可能只有10-20g,依然可以调大。
hlogs上限提高能解决重启后同时flush密集的问题。重启后,各region memstore都是从0开始缓存,hlogs到上限时如果大量region没写满,会触发大量region同时 flush,这些region的memstore再次清零了,下个周期依然有大量region同时flush,打散速度慢于刷新周期触发的flush。hlogs上限出发的同时flush,compact对gc压力很大,请求延时会周期性显著提升。如果256MB flush,8台服务器,hlogs需要到400。128MB则200即可。
hlogs过多可能有什么影响?1.重启时间变长。重试时间长本质是总memstore增大,flush时间长造成的。之前17台规模单台重启大概1分钟,8台规模单台大概1分50秒,总时间并没有增加。再就是如果某台regionserver挂掉,集群要重读未flush的hlogs,hlogs多了会增加重读的量,增加集群负担。

综上所述,最终flush size定为128MB,hlogs上限定为200。

读cache
hfile.block.cache.size 0.19 ->0.2
hbase.bucketcache.size 102400 -> 132000
hbase.bucketcache.percentage.in.combinedcache 0.9 -> 0.85

缓存大小配合堆内堆外内存增加的调整,缓存变成堆外上限112g,堆内20g。L1和L2的比例按实际线上情况由0.9调整为0.85.


compact
hbase.regionserver.thread.compaction.small 6 -> 8
hbase.regionserver.thread.compaction.large 3 -> 4

略微提高minor compact 和major compact速度,尤其是major compact速度,以便机器减少到一半时,夜里能major完。调到12,gc压力过大,所以只是微调。

hdfs
dfs.client.hedged.read.threadpool.size 50 ->300 
dfs.client.hedged.read.threshold.millis 150->500
hbase.regionserver.hlog.slowsync.ms 无->400
compact负载一高,200线程池会报大量的线程池满,资源不够用,所以调到300。
我们用多路读是为了当磁盘故障时,可以读其他副本。如果超时时间太低,可以读本地的去读了远程副本,显著增大集群网络和磁盘IO。读包括compact的读,是轻延时重吞吐的,集群磁盘IO负载高,延时增加,触发多路读又增大了集群的IO压力。尤其是本地化不是100%时,会读其他机器上的副本,400毫秒也依然容易超时,所以超时时间改为500,确保在一般的高负载和非本地化场景中,也不会给集群额外的压力,只有磁盘真正故障堵塞读的时候再读其他副本上。由于95%以上的读都来自内存缓存,500毫秒的最大超时时间并不会造成显著的读请求延时升高,毕竟常态的gc也要几百ms时间。
负载稍微高点,日志文件满屏都是wal log slow,淹没了其他需要关注的问题。由于wal是单线程顺序写单文件,写入速度是有瓶颈的,调到400,只有负载较高时才会打印。

gc参数调整
请看上一篇,本文不再叙述。

未来的工作
近期工作
打散major compact执行时间

现在集群内所有业务分组同一天晚上进行major compact ,集群网络和磁盘IO压力大,存储量也会同时飙升。major compact即将打散到每周7天的晚上,以降低集群的压力。

换compact policy
compact policy优化,需要用户配合做客户端代码优化,和业务方暂定4月初共建。
现在所有业务用的都是默认的default policy,中规中矩。而该项目业务场景非常适合用Date Tiered Compaction,该策略能极大降低compact写放大效应尤其是major compact的压力,并且提升近期热点数据查询速度。
更换该策略,需要业务方略微修改客户端代码,读写时增加TTL的设置,否则可能会降低查询速度。

换压缩算法
换高压缩比算法gz能进一步减少存储总量,已有经验是可以降低30%左右。即存储量视角,可以降到7台服务器规模。换压缩算法会增加cpu利用率,可能对用户读写造成未知的影响,待4月和用户共建。


中远期工作
2副本

好处是存储量立刻降低1/3,坏处是集群同时坏2块盘的概率比坏3块高得多,更容易丢数据。一般是偏离线,稳定性要求不高,存储量偏高的业务,数据在hive有一份,即使有数据缺失可以很快从hive导一份。需要和用户进一步确定业务模式是否能采用。

超卖
目前成本分摊策略,独立业务分组按regionserver服务器数分摊成本,如果独立业务分组存储总量大于分组机器所提供的存储总量,即认为存储量达到瓶颈。就必须要增加机器,不同的业务瓶颈不一样,按存储量严格限制,不利于提升集群资源的整体利用率。
所以从整个集群的资源利用率来看,允许部分吞吐,延时要求不高的业务使用更多的存储,即存储超卖,可以更有效提高集群资源利用率,释放数据引擎潜力,降低业务方使用成本,为公司省钱。
超卖涉及三个维度。超卖存储每TB价格,超卖存储量如何计算。超卖比例,
现在每TB成本是整机打包折算的,而超卖的存储只涉及纯磁盘的折算成本。要超卖存储,需要额外提供超卖存储的每TB价格,这个价格应该比整机的每TB成本低一些。
超卖存储量可以用定时程序收集分组的hdfs存储量,保留最高值,除0.9作为该月实际存储用量,减去分组机器数提供的存储就是超卖量。
超卖比例,前期可以先允许超卖分组存储的20%,并监控集群整体磁盘利用率情况。超卖试运行一段时间后,如果集群存储依然空闲,可以尝试提高超卖比例上限。
目前成本分摊的方式,只支持业务分组按整机打包折算,没有超卖的分摊方式。待评估实际需求后,再来决定是否允许超卖。
如果有业务方需要过高的超卖比例,不适用于现在的集群架构,需要考虑ssd和sata混部的架构。

ssd和sata混部
目前业务按数据量和期望延时的不同分了两个集群,1个是ssd集群,一个是sata盘集群。ssd读写更快,但成本高,存储量有限。
业内最顶尖的做法,是允许ssd和sata盘混部,好处有2。一个是3副本一个是ssd2个是sata盘,保证性能的同时降低存储成本。另一个是时间相关数据按时间分块存储在不同介质上,例如支付宝账单,3个月内ssd,1年内sata盘,一年前的历史数据就存储在更便宜的存储介质上了。历史类数据都适合用这种方式。
混部的方式,在软件,集群架构,机器采购方面都有颇多挑战,是个长期的工作。

有关导出数据问题

hbase过往记忆 回复了问题 • 3 人关注 • 3 个回复 • 311 次浏览 • 2019-03-21 18:19 • 来自相关话题

hbase regionserver启动不了

hbasezb 回复了问题 • 3 人关注 • 3 个回复 • 312 次浏览 • 2019-03-21 14:11 • 来自相关话题

(1.3版)hbase优化之旅(三)-regionserver g1 gc执行细节和参数调优方法论详解

hbasechuxiao 发表了文章 • 0 个评论 • 453 次浏览 • 2019-03-14 17:43 • 来自相关话题

release note
1.3版:调整章节顺序,略微修改概述和标题
1.2版:增加目录信息
1.1版 :增加问题12,补充gc问题挂进程和优化方法论总结的章节,解释最终优化内容。
 
 
目录
本文亮点
gc调优效果
学习的起点-知道自己不知道what
g1 gc执行细节
参数调优方式论
增加日志打印
分析统计信息
分析gc日志
gc问题挂进程
1.写入过猛引起的进程挂
2.参数问题导致mixed gc连续出现长时间暂停
优化方法论小结
最终优化内容

 
本文亮点

讲g1原理和参数语义的文章很多,随便谷歌百度一下无数。但大多数都是对官方介绍的翻译转述。很少有文章介绍参数间彼此的影响,调整参数牵一发动全身的影响有哪些。参数调优的方法论,更是没人提及。

本文目标受众是对g1原理和参数有所了解,想进一步了解参数彼此间联系,想在参数调优方面更进一步的同学。

通过一个线上gc优化的实际案例,带你熟悉gc执行细节,了解我总结的参数调优方法论。

如果对g1不了解,可以先搜索了解一下g1原理和参数后再来看本文,会事半功倍。


gc调优效果

线上某分组的regionserver之前存在不稳定的问题。一两个月内总会随机出现1,2台机器,突然cpu飚高,写延时变高引起堆积,hbase日志和监控找不到原因,只能将问题regionserver移走换一台服务器代替。后来发现regionserver直接重启也能解决,怀疑regionserver配置有问题。

经调研和论证,确定是gc参数配置问题,参数优化后彻底解决了该问题。

由于服务器上还部署了datanode,存在一定的资源抢占,regionserver gc时间,间隔有一些波动,但大体是可衡量的。

之前50g堆,young区最大15g,6秒一次young gc,16线程暂停时间100ms左右。 现在100g堆,控制 20g young区,间隔8秒一次young gc,32线程暂停时间大概90ms。

优化前,大概7分钟一次mixed gc周期,优化后大概半小时。

优化前,mixed gc周期内头几次mixed gc之后,mixed gc快速恶化到400-1500毫秒之间,收尾的几次gc基本要秒级以上,gc时间占比短期内达到80%。优化后,mixed gc绝大多数在400ms以内,百分之几的概率在500-900毫秒范围,gc时间基本不会触发超过10%的告警日志打印。


学习的起点-知道自己不知道what 

看过文档,知道参数语义了,就可以进行参数调优了吗?

学习有这么几个阶段,什么都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在“不知道自己不知道”的阶段,误以为自己什么都知道,贸然调优,事倍功半,优化结果南辕北辙。

请看下面的问题,是否有明确的答案?


1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young区起始比例和最大比例的默认值。那么young区有最小比例吗,最小比例是多少?

2.young区的动态大小除了受-XX:MaxGCPauseMillis=100 单次gc最大暂停时间影响,受-XX:G1MaxNewSizePercent=60上限限制,还被其他因素影响吗?

3.-XX:InitiatingHeapOccupancyPercent=45是启动并发mixed gc的已用内存比例阈值,该比例的分母是当前堆内存。那么分子是old区已用,是young+old区已用,还是堆内存已用?

4.一次mixed gc周期内,mixed gc之间的间隔是怎样的,立刻执行下次,有固定时间间隔,还是受其他条件影响?

5.-XX:G1OldCSetRegionThresholdPercent=10 是单次mixed gc扫old区region数的比例。该比例的分母是old区已用region,young+old区已用region,还是堆总region?该比例设置10就会扫10%的region吗,是否受其他条件影响?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目标次数,该数值实际逻辑是什么,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)要回收region的存活对象内存占比,存活越低回收效果越好。如果存活比例超过85%了,这次mixed gc,这次mixed迭代周期内就一定不会回收了吗?

8.很多技术文章都翻译了官方文档的这句话,mixed gc从可回收比例高的region开始回收。实际执行上是怎么做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次数, -XX:G1OldCSetRegionThresholdPercent=10 是最大扫old区region数的比例,-XX:MaxGCPauseMillis=100 是期待的单次gc最大暂停时间。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)是回收region的存活对象内存占比。这几个参数如何决定单次mixed gc清理多少region,一次mixed周期执行多少次mixed gc?

10.其他条件一样,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的单次gc时间短吗?

11.阅读理解,下面三行mixed gc执行日志,说明存在哪些问题?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]
6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]
6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

 
12.坊间传闻,g1内存越大回收效率越差,越不稳定。即使机器资源够分配100g的堆,也只分配50g。这么做有道理吗?

上面各个参数都标注了网上常见的参数解释,了解参数解释你能回答以上的问题吗?如果有疑惑,那么请看下一节。

 


g1 gc执行细节

-XX:G1NewSizePercent同时是young区最小比例。

young区上限还受old区大小影响,最大不超过90%(默认保留10%)-当前old区大小。当old区朝90%接近时,young区会持续减少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old区已用。

mixed gc之间是以触发下限young区gc为间隔的,即下限eden区写满时,mixed gc同时清理young区和old区。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆总region数,100g堆32MB大小region,则总数3200个region,10%就是320个。该比例决定单次mixed gc扫描region数的上限。如果预期扫region时间高于预期剩余时间,则只会扫少量region计算活跃对象比例。

-XX:G1MixedGCCountTarget=8语义是,用触发mixed gc周期时old区的已用region数,除以8,作为每次mixed gc逻辑上最少回收的region数。

一次mixed gc扫描的region中,活跃对象比例高于-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代价高。

如果不昂贵的region数不小于最小回收数,那么回收所有不昂贵region,昂贵region按活跃比例在mixed gc周期内整体排序。如果扫描完所有old区region,垃圾比例依然高于-XX:G1HeapWastePercent比例,那么按活跃对象比逆序清理昂贵region,直到比例降低到阈值。

如果总region数大于最小region数但不昂贵region数不够min,则回收min个region,昂贵region中活跃对象比最低的region填补min的缺。

如果min大于实际扫的region,会回收本次mixed gc所有扫瞄过的region数,即使region的活跃对象比超过阈值。

如果-XX:MaxGCPauseMillis过低,预期扫region时间远大于预期剩余时间,那么实际扫的region会小于min数,即使扫的都是昂贵region,依然会全部回收,造成数秒级gc暂停,实际暂停时间反而比-XX:MaxGCPauseMillis大一些要长。

若干次mixed gc后,如果可回收占堆内存比例小于等于-XX:G1HeapWastePercent,本轮mixed gc周期结束。

 

综上所述,参数有密切的关联关系,调优需要全局权衡。

最后一个问题的日志,由于-XX:MaxGCPauseMillis过低只扫描了少量region,-XX:G1MixedGCCountTarget过低min region数高,昂贵region依然要被回收,暂停时间远大于预期到达秒级,下次扫的region更少,回收昂贵region难以释放内存,持续恶化。堆50g,young区下限2.5g ,间隔不到1秒一次mixed gc,gc时间占比很快超过80%。再加上偶发的memstore内存接近峰值,加上L1读cache,加上静态对象占用的,总不可释放内存比例很高,而-XX:InitiatingHeapOccupancyPercent比例过低,触发mixed gc周期时几乎拷贝了一遍old区所有region,内存也没释放多少空间,regionserver表现出持续的吞吐能力降低,服务不可用现象。
 
目前只遗留了一个问题,g1是否可以用大堆。容我卖个关子,读者可以结合上面的执行细节,先自己认真思考一下,再往下看。

 
参数调优方式论

授人以鱼不如授人以渔,我们先来探讨调优的方法论。

调优方法论是,先整体分析gc运行状况,找到瓶颈点或怀疑有问题的地方。仔细翻阅问题发生时间的gc日志,找到有问题的信息,调优。继续观察。


增加日志打印

调优首先要清楚gc运行状况,上一篇gc探索分享里介绍了如何加打印参数,以及如何通过gceasy可视化统计信息。如果没阅读请先看上一篇相关内容。需要额外注意的是,gceasy对g1支持的有点小bug,gc暂停时间图把mixed gc统计到young gc次数里了。如有图里有暂停时间比-XX:MaxGCPauseMillis高一个数量级的暂停时间,都是mixed gc的。

 
分析统计信息

通过gceasy,我们可以看到gc平均暂停时间,最大暂停时间,应用吞吐占比。gc暂停时间分布,gc前后堆内存变化,gc暂停时间按时间周期的分布图,单次gc垃圾回收内存量的分布图,young区容量分布图,old区容量分布图,young区 promoted分布图,gc内部各阶段时间统计对比,对象创建和升代速度。

通过这些统计信息我们可以了解系统的运行情况,如young 区在多大范围波动,平均young区 gc间隔大概是多久,升代量是多少,mixed gc周期的间隔,每次mixed gc周期回收的old 区内存,等等。掌握这些统计信息,我们就对系统运行情况有了基本了解,可以对系统是否健康做一个初步判断。

不健康主要有两大类,单次暂停时间过长,gc时间占比高。这两个问题又可以引申出单次gc暂停时间过长的次数太多,gc时间占比过高的频率高,mixed gc频率高,每次mixed回收old区内存量太低,等等。

我的经验是,50g或100g的堆内存,如果gc时间占比超过5%,或者gc最大暂停时间超过5秒出现很多次,都是有问题的,需要优化。

如果gc日志跨越好几天,周期性gc时间占比高,但平均gc时间占比可能并不高。如果某一时间段吞吐,延时有问题,可以将这个时间段前后半小时的gc日志截出来,单独上传gceasy。

只看统计信息还不够。本人遇到过日志周期跨越好几天,平均gc时间占比不到2%,最大暂停时间1秒110毫秒,但图表上来看1秒上下的暂停总是连续出现,伴随着周期性业务响应变慢。仔细查看gc日志,gc时间占比在短时间内达到了70%以上,周期性出现。所以需要进一步分析gc日志。


分析gc日志

gc日志内容很多,包括每次gc前堆情况,gc后堆情况,young和mixed gc执行情况。那么我们如何分析呢?

还是要对症下药,找问题出现时前后的日志。

如果暂停时间周期性或偶发出现比预期高一个数量级,可以根据统计信息看到的长时间暂停时间。搜日志,如搜索 real=5找暂停时间5秒到6秒的 ,找到gc上下文,分析为什么慢。


常见以下几种问题

1. max小于等于min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]
2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]
2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

没啥说的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的经验是min最好控制在max的1/4以内,留冗余给昂贵region。

 

2.predicted time is too high

 60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 regions]
 
 60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time:0.00 ms]

 

由于期望gc执行时间短,预期时间太长,只会扫很少的old区region,甚至可能比min region还少,当遇到连续的高代价region,即使是100%活跃的region也要拷贝,执行时间更长,下次预期时间更长,扫的region数更少,进入恶性循环。单次mixed gc释放不了多少内存,gc时间占比越来越高,有zk超时风险。

本质上,这是设置期望时间太短反而造成暂停时间更长,需要放宽期望gc执行时间,减少young 区最小值,以增大回收old区的可用时间。降低-XX:G1OldCSetRegionThresholdPercent比例以降低预期时间。内存使用上,让不可回收内存比例低一些,避免高存活比例region连续出现的概率,即增大堆内存,增大old区回收阈值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低写放大,降低磁盘读写IO,增大L1缓存可以提高读缓存命中率。所以这不单单是gc自己的问题,要系统性综合考虑,确定系统的瓶颈究竟是什么,优化哪个问题更重要。

 

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytes (15.00 %), threshold: 15.00 %]
 
 61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time:0.00 ms]

 
到达垃圾保留比例,最后一次mixed gc只会扫很少的region,如果正好都是昂贵的region,则拷贝代价很高。
运气不好的话,这几乎是不可避免的。调大-XX:G1OldCSetRegionThresholdPercent理论上可以让最后一次扫region的平均数量变大,但会造成predicted time is too high更频繁出现。增加堆内存上限和old区回收阈值,提高-XX:G1HeapWastePercent比例,可以更早结束垃圾mixed gc周期,最后一次扫描都是昂贵region的概率也降低了。调大-XX:G1MixedGCCountTarget 让min region更少,可能每次回收量减少一次回收周期时间拉长,需要配合更高的垃圾浪费率和更低的-XX:G1MixedGCLiveThresholdPercent比例,达到快速清理的效果。


gc问题挂进程

gc问题的极端后果是进程挂掉。一般经验认为,内存增加比释放快,内存不足,full gc, oom,进程就挂了。

我遇到过多次gc引起进程挂掉,但目前还没遇到过g1的oom,甚至都没遇到过g1的full gc。这是因为regionserver内存模型young区升代比例很低,另外g1在恶劣条件下gc时间占比很高,即使regionserver压力很大,还没到full gc,就gc时间占比过高引起zk session超时退出了。下面举两个例子。

1.写入过猛引起的进程挂

业务方补一年的数据,hadoop作业写入过猛,还有热点,flush一直排队,compact一直排队,甚至触发hfile上限堵塞写了。写的p99一度飙升到30秒,young gc一次升old区很多,old区内存增长比正常快。结果还没到old区触发mixed gc,由于young gc达到了1秒2,3次,gc时间占比一度超过了95%,开始出现zk session超时,regionserver自动退出。

可以调大region hfile数上限来缓解,但治标不治本。需要控制用户写入,加quota来限制。

 

2.参数问题导致mixed gc连续出现长时间暂停

regionserver有一定压力,在承受范围内而进程挂了。这是由于参数设置有问题,由于期待暂停时间过低扫的region数不够多,又都是不可回收region,暂停时间越来越长,几次达到8,9秒暂停后,zk session超时退出。

这个按上面的redicted time is too high问题来优化即可。

 

优化方法论小结

1.-XX:ParallelGCThreads官方推荐是逻辑cpu核数的5/8,注意逻辑cpu核数是物理核的2倍,所以24核可以开到32,young和mixed gc确实变快了。

2.参数调整确保每轮mixed gc的max region数是min region数的4倍以上,降低都是昂贵region的几率。

3.适量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暂停时间。目的是留给扫描region充足时间,确保每轮mixed gc扫描的region数和期待的max region数相似。

4.如果不想young gc时间也同步变长,可以通过-XX:G1MaxNewSizePercent降低young区最大比例来控制young gc时间。

5.降低最小young 区比例,可以降低mixed gc时回收young 区的时间,从而增加扫描old区region时间,确保扫描更多region。

6.触发mixed gc周期时,old区可回收内存比例越高,越不容易遇到连续昂贵ergion,回收越有效率。所以应该调大堆内存,调高mixed gc触发阈值,控制不可回收内存比例(即memstore和L1 block cache)。

7.当前面条件都满足时,每次mixed gc周期可回收内存比例很高,每轮mixed gc扫描的region数几倍于min region有充足的region挑选不昂贵ergion,可以调高-XX:G1HeapWastePercent比例让本轮mixed gc尽快结束,降低-XX:G1MixedGCLiveThresholdPercent优先回收活跃对象更少的region。


最终优化内容

-Xmx100g -Xms100g   50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent=   65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

 

 

 
  查看全部
release note
1.3版:调整章节顺序,略微修改概述和标题
1.2版:增加目录信息
1.1版 :增加问题12,补充gc问题挂进程和优化方法论总结的章节,解释最终优化内容。
 
 
目录
本文亮点
gc调优效果
学习的起点-知道自己不知道what
g1 gc执行细节
参数调优方式论
增加日志打印
分析统计信息
分析gc日志
gc问题挂进程
1.写入过猛引起的进程挂
2.参数问题导致mixed gc连续出现长时间暂停
优化方法论小结
最终优化内容

 
本文亮点

讲g1原理和参数语义的文章很多,随便谷歌百度一下无数。但大多数都是对官方介绍的翻译转述。很少有文章介绍参数间彼此的影响,调整参数牵一发动全身的影响有哪些。参数调优的方法论,更是没人提及。

本文目标受众是对g1原理和参数有所了解,想进一步了解参数彼此间联系,想在参数调优方面更进一步的同学。

通过一个线上gc优化的实际案例,带你熟悉gc执行细节,了解我总结的参数调优方法论。

如果对g1不了解,可以先搜索了解一下g1原理和参数后再来看本文,会事半功倍。


gc调优效果

线上某分组的regionserver之前存在不稳定的问题。一两个月内总会随机出现1,2台机器,突然cpu飚高,写延时变高引起堆积,hbase日志和监控找不到原因,只能将问题regionserver移走换一台服务器代替。后来发现regionserver直接重启也能解决,怀疑regionserver配置有问题。

经调研和论证,确定是gc参数配置问题,参数优化后彻底解决了该问题。

由于服务器上还部署了datanode,存在一定的资源抢占,regionserver gc时间,间隔有一些波动,但大体是可衡量的。

之前50g堆,young区最大15g,6秒一次young gc,16线程暂停时间100ms左右。 现在100g堆,控制 20g young区,间隔8秒一次young gc,32线程暂停时间大概90ms。

优化前,大概7分钟一次mixed gc周期,优化后大概半小时。

优化前,mixed gc周期内头几次mixed gc之后,mixed gc快速恶化到400-1500毫秒之间,收尾的几次gc基本要秒级以上,gc时间占比短期内达到80%。优化后,mixed gc绝大多数在400ms以内,百分之几的概率在500-900毫秒范围,gc时间基本不会触发超过10%的告警日志打印。


学习的起点-知道自己不知道what 

看过文档,知道参数语义了,就可以进行参数调优了吗?

学习有这么几个阶段,什么都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在“不知道自己不知道”的阶段,误以为自己什么都知道,贸然调优,事倍功半,优化结果南辕北辙。

请看下面的问题,是否有明确的答案?


1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young区起始比例和最大比例的默认值。那么young区有最小比例吗,最小比例是多少?

2.young区的动态大小除了受-XX:MaxGCPauseMillis=100 单次gc最大暂停时间影响,受-XX:G1MaxNewSizePercent=60上限限制,还被其他因素影响吗?

3.-XX:InitiatingHeapOccupancyPercent=45是启动并发mixed gc的已用内存比例阈值,该比例的分母是当前堆内存。那么分子是old区已用,是young+old区已用,还是堆内存已用?

4.一次mixed gc周期内,mixed gc之间的间隔是怎样的,立刻执行下次,有固定时间间隔,还是受其他条件影响?

5.-XX:G1OldCSetRegionThresholdPercent=10 是单次mixed gc扫old区region数的比例。该比例的分母是old区已用region,young+old区已用region,还是堆总region?该比例设置10就会扫10%的region吗,是否受其他条件影响?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目标次数,该数值实际逻辑是什么,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)要回收region的存活对象内存占比,存活越低回收效果越好。如果存活比例超过85%了,这次mixed gc,这次mixed迭代周期内就一定不会回收了吗?

8.很多技术文章都翻译了官方文档的这句话,mixed gc从可回收比例高的region开始回收。实际执行上是怎么做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次数, -XX:G1OldCSetRegionThresholdPercent=10 是最大扫old区region数的比例,-XX:MaxGCPauseMillis=100 是期待的单次gc最大暂停时间。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)是回收region的存活对象内存占比。这几个参数如何决定单次mixed gc清理多少region,一次mixed周期执行多少次mixed gc?

10.其他条件一样,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的单次gc时间短吗?

11.阅读理解,下面三行mixed gc执行日志,说明存在哪些问题?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]
6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]
6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

 
12.坊间传闻,g1内存越大回收效率越差,越不稳定。即使机器资源够分配100g的堆,也只分配50g。这么做有道理吗?

上面各个参数都标注了网上常见的参数解释,了解参数解释你能回答以上的问题吗?如果有疑惑,那么请看下一节。

 


g1 gc执行细节

-XX:G1NewSizePercent同时是young区最小比例。

young区上限还受old区大小影响,最大不超过90%(默认保留10%)-当前old区大小。当old区朝90%接近时,young区会持续减少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old区已用。

mixed gc之间是以触发下限young区gc为间隔的,即下限eden区写满时,mixed gc同时清理young区和old区。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆总region数,100g堆32MB大小region,则总数3200个region,10%就是320个。该比例决定单次mixed gc扫描region数的上限。如果预期扫region时间高于预期剩余时间,则只会扫少量region计算活跃对象比例。

-XX:G1MixedGCCountTarget=8语义是,用触发mixed gc周期时old区的已用region数,除以8,作为每次mixed gc逻辑上最少回收的region数。

一次mixed gc扫描的region中,活跃对象比例高于-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代价高。

如果不昂贵的region数不小于最小回收数,那么回收所有不昂贵region,昂贵region按活跃比例在mixed gc周期内整体排序。如果扫描完所有old区region,垃圾比例依然高于-XX:G1HeapWastePercent比例,那么按活跃对象比逆序清理昂贵region,直到比例降低到阈值。

如果总region数大于最小region数但不昂贵region数不够min,则回收min个region,昂贵region中活跃对象比最低的region填补min的缺。

如果min大于实际扫的region,会回收本次mixed gc所有扫瞄过的region数,即使region的活跃对象比超过阈值。

如果-XX:MaxGCPauseMillis过低,预期扫region时间远大于预期剩余时间,那么实际扫的region会小于min数,即使扫的都是昂贵region,依然会全部回收,造成数秒级gc暂停,实际暂停时间反而比-XX:MaxGCPauseMillis大一些要长。

若干次mixed gc后,如果可回收占堆内存比例小于等于-XX:G1HeapWastePercent,本轮mixed gc周期结束。

 

综上所述,参数有密切的关联关系,调优需要全局权衡。

最后一个问题的日志,由于-XX:MaxGCPauseMillis过低只扫描了少量region,-XX:G1MixedGCCountTarget过低min region数高,昂贵region依然要被回收,暂停时间远大于预期到达秒级,下次扫的region更少,回收昂贵region难以释放内存,持续恶化。堆50g,young区下限2.5g ,间隔不到1秒一次mixed gc,gc时间占比很快超过80%。再加上偶发的memstore内存接近峰值,加上L1读cache,加上静态对象占用的,总不可释放内存比例很高,而-XX:InitiatingHeapOccupancyPercent比例过低,触发mixed gc周期时几乎拷贝了一遍old区所有region,内存也没释放多少空间,regionserver表现出持续的吞吐能力降低,服务不可用现象。
 
目前只遗留了一个问题,g1是否可以用大堆。容我卖个关子,读者可以结合上面的执行细节,先自己认真思考一下,再往下看。

 
参数调优方式论

授人以鱼不如授人以渔,我们先来探讨调优的方法论。

调优方法论是,先整体分析gc运行状况,找到瓶颈点或怀疑有问题的地方。仔细翻阅问题发生时间的gc日志,找到有问题的信息,调优。继续观察。


增加日志打印

调优首先要清楚gc运行状况,上一篇gc探索分享里介绍了如何加打印参数,以及如何通过gceasy可视化统计信息。如果没阅读请先看上一篇相关内容。需要额外注意的是,gceasy对g1支持的有点小bug,gc暂停时间图把mixed gc统计到young gc次数里了。如有图里有暂停时间比-XX:MaxGCPauseMillis高一个数量级的暂停时间,都是mixed gc的。

 
分析统计信息

通过gceasy,我们可以看到gc平均暂停时间,最大暂停时间,应用吞吐占比。gc暂停时间分布,gc前后堆内存变化,gc暂停时间按时间周期的分布图,单次gc垃圾回收内存量的分布图,young区容量分布图,old区容量分布图,young区 promoted分布图,gc内部各阶段时间统计对比,对象创建和升代速度。

通过这些统计信息我们可以了解系统的运行情况,如young 区在多大范围波动,平均young区 gc间隔大概是多久,升代量是多少,mixed gc周期的间隔,每次mixed gc周期回收的old 区内存,等等。掌握这些统计信息,我们就对系统运行情况有了基本了解,可以对系统是否健康做一个初步判断。

不健康主要有两大类,单次暂停时间过长,gc时间占比高。这两个问题又可以引申出单次gc暂停时间过长的次数太多,gc时间占比过高的频率高,mixed gc频率高,每次mixed回收old区内存量太低,等等。

我的经验是,50g或100g的堆内存,如果gc时间占比超过5%,或者gc最大暂停时间超过5秒出现很多次,都是有问题的,需要优化。

如果gc日志跨越好几天,周期性gc时间占比高,但平均gc时间占比可能并不高。如果某一时间段吞吐,延时有问题,可以将这个时间段前后半小时的gc日志截出来,单独上传gceasy。

只看统计信息还不够。本人遇到过日志周期跨越好几天,平均gc时间占比不到2%,最大暂停时间1秒110毫秒,但图表上来看1秒上下的暂停总是连续出现,伴随着周期性业务响应变慢。仔细查看gc日志,gc时间占比在短时间内达到了70%以上,周期性出现。所以需要进一步分析gc日志。


分析gc日志

gc日志内容很多,包括每次gc前堆情况,gc后堆情况,young和mixed gc执行情况。那么我们如何分析呢?

还是要对症下药,找问题出现时前后的日志。

如果暂停时间周期性或偶发出现比预期高一个数量级,可以根据统计信息看到的长时间暂停时间。搜日志,如搜索 real=5找暂停时间5秒到6秒的 ,找到gc上下文,分析为什么慢。


常见以下几种问题

1. max小于等于min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]
2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]
2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

没啥说的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的经验是min最好控制在max的1/4以内,留冗余给昂贵region。

 

2.predicted time is too high

 60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 regions]
 
 60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time:0.00 ms]

 

由于期望gc执行时间短,预期时间太长,只会扫很少的old区region,甚至可能比min region还少,当遇到连续的高代价region,即使是100%活跃的region也要拷贝,执行时间更长,下次预期时间更长,扫的region数更少,进入恶性循环。单次mixed gc释放不了多少内存,gc时间占比越来越高,有zk超时风险。

本质上,这是设置期望时间太短反而造成暂停时间更长,需要放宽期望gc执行时间,减少young 区最小值,以增大回收old区的可用时间。降低-XX:G1OldCSetRegionThresholdPercent比例以降低预期时间。内存使用上,让不可回收内存比例低一些,避免高存活比例region连续出现的概率,即增大堆内存,增大old区回收阈值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低写放大,降低磁盘读写IO,增大L1缓存可以提高读缓存命中率。所以这不单单是gc自己的问题,要系统性综合考虑,确定系统的瓶颈究竟是什么,优化哪个问题更重要。

 

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytes (15.00 %), threshold: 15.00 %]
 
 61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time:0.00 ms]

 
到达垃圾保留比例,最后一次mixed gc只会扫很少的region,如果正好都是昂贵的region,则拷贝代价很高。
运气不好的话,这几乎是不可避免的。调大-XX:G1OldCSetRegionThresholdPercent理论上可以让最后一次扫region的平均数量变大,但会造成predicted time is too high更频繁出现。增加堆内存上限和old区回收阈值,提高-XX:G1HeapWastePercent比例,可以更早结束垃圾mixed gc周期,最后一次扫描都是昂贵region的概率也降低了。调大-XX:G1MixedGCCountTarget 让min region更少,可能每次回收量减少一次回收周期时间拉长,需要配合更高的垃圾浪费率和更低的-XX:G1MixedGCLiveThresholdPercent比例,达到快速清理的效果。


gc问题挂进程

gc问题的极端后果是进程挂掉。一般经验认为,内存增加比释放快,内存不足,full gc, oom,进程就挂了。

我遇到过多次gc引起进程挂掉,但目前还没遇到过g1的oom,甚至都没遇到过g1的full gc。这是因为regionserver内存模型young区升代比例很低,另外g1在恶劣条件下gc时间占比很高,即使regionserver压力很大,还没到full gc,就gc时间占比过高引起zk session超时退出了。下面举两个例子。

1.写入过猛引起的进程挂

业务方补一年的数据,hadoop作业写入过猛,还有热点,flush一直排队,compact一直排队,甚至触发hfile上限堵塞写了。写的p99一度飙升到30秒,young gc一次升old区很多,old区内存增长比正常快。结果还没到old区触发mixed gc,由于young gc达到了1秒2,3次,gc时间占比一度超过了95%,开始出现zk session超时,regionserver自动退出。

可以调大region hfile数上限来缓解,但治标不治本。需要控制用户写入,加quota来限制。

 

2.参数问题导致mixed gc连续出现长时间暂停

regionserver有一定压力,在承受范围内而进程挂了。这是由于参数设置有问题,由于期待暂停时间过低扫的region数不够多,又都是不可回收region,暂停时间越来越长,几次达到8,9秒暂停后,zk session超时退出。

这个按上面的redicted time is too high问题来优化即可。

 

优化方法论小结

1.-XX:ParallelGCThreads官方推荐是逻辑cpu核数的5/8,注意逻辑cpu核数是物理核的2倍,所以24核可以开到32,young和mixed gc确实变快了。

2.参数调整确保每轮mixed gc的max region数是min region数的4倍以上,降低都是昂贵region的几率。

3.适量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暂停时间。目的是留给扫描region充足时间,确保每轮mixed gc扫描的region数和期待的max region数相似。

4.如果不想young gc时间也同步变长,可以通过-XX:G1MaxNewSizePercent降低young区最大比例来控制young gc时间。

5.降低最小young 区比例,可以降低mixed gc时回收young 区的时间,从而增加扫描old区region时间,确保扫描更多region。

6.触发mixed gc周期时,old区可回收内存比例越高,越不容易遇到连续昂贵ergion,回收越有效率。所以应该调大堆内存,调高mixed gc触发阈值,控制不可回收内存比例(即memstore和L1 block cache)。

7.当前面条件都满足时,每次mixed gc周期可回收内存比例很高,每轮mixed gc扫描的region数几倍于min region有充足的region挑选不昂贵ergion,可以调高-XX:G1HeapWastePercent比例让本轮mixed gc尽快结束,降低-XX:G1MixedGCLiveThresholdPercent优先回收活跃对象更少的region。


最终优化内容

-Xmx100g -Xms100g   50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent=   65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

 

 

 
 

Flink + HBase 如何实现实时统计pv uv

hbasemachuan 回复了问题 • 2 人关注 • 1 个回复 • 1057 次浏览 • 2019-03-10 22:41 • 来自相关话题

hbase优化之旅(二)regionserver的G1 GC优化探索

hbasechuxiao 发表了文章 • 1 个评论 • 547 次浏览 • 2019-03-06 17:09 • 来自相关话题

优化的最终目的是保障现有用户体验的同时,减少机器,节约成本。

为了更好的编写本文和后续文章,花费20美金。欢迎去附录链接专栏赞赏支持。

g1介绍
g1特点

g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。

g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在预期范围内。

g1适用场景

对平均响应时间,最大响应时间有严格要求的应用系统,如hbase regionserver。

优化原则
先优化业务层和应用层

系统调优是从业务到实现,从整体到局部,从架构到具体组件的。在进行gc调优之前,我们应该确保业务层和应用层已经评估优化过。业务层和应用层的优化一般来说更容易有收益,我们不能指望一个架构设计有缺陷,应用层代码有很多已知问题的系统,通过gc调优一劳永逸。

gc调优3选2原则

先来看一下衡量gc的指标有哪些。对应用吞吐量的影响(一般是gc对cpu的消耗),对延迟的影响,总内存占用(gc触发时留有内存业务可以继续,留有内存做对象拷贝碎片整理等操作,不能oom)。

GC调优3选2原则: 在吞吐量、延迟、内存占用上,我们只能选择其中两个进行调优,无法三者兼得。

hbase已有业务regionserver的调优目标

在调优之前,必须要有明确的性能优化目标, 然后找到未达到该目标的性能瓶颈。再针对瓶颈做优化。通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

hbase集群启用了group分组,重要业务有独立的regionserver分组。

重要业务regionserver的调优目标是,在满足业务延迟要求的基础上,用尽量低的成本,满足业务吞吐量的峰值需求。

也就是说,总吞吐量固定,延迟要求固定,单机cpu和内存固定,求最小机器数。

再转换一下,对单机来说,延迟指标确定,将单机吞吐在单机cpu和内存允许的范围内调整到最大。

需要说明的是,单机能承担多少吞吐,跟业务访问模型,region数,读写缓存参数,网络IO,磁盘IO都有关系。业务和hbase参数的调整应该在gc优化之前进行,网络和磁盘IO一般是应用层优化的。所以下文假设业务层和应用层已优化完毕,网络和磁盘都不是瓶颈,只聚焦在gc参数调优。

本文假设我们换算后的延迟目标是平均gc暂停时间100ms,最大暂停时间2s,gc时间占比3%以内。实际达到这个目标后,还要通过regionserver监控确定请求的延时要是否在用户用户的要求范围内。

影响延迟的因素

gc的时间占比。平均stw gc时间,频率。毛刺stw gc时间,频率。峰值stw gc时间,频率。

一般来说,regionserver应该避免full gc。

新生代越大,单次young gc时间越长,频率越低。

mixed gc受gc触发时机,gc并发线程数,预期迭代次数,每个迭代回收分片比例等多个参数影响,详见附录官方文档。

关于JVM版本

目前生产环境用1.8.0_77, 小米hbase环境用1.8.0_111, Oracle jdk的8最新版本是8u201。

intel性能测试见附录,jdk7不同版本间g1性能差距很大。Jdk7u21升级到jdk7u60,gc以及stw gc的平均时间,最大时间,时间分布都有大幅优化。

所以应该尽量用最新版本的JDK。

优化步骤

需要有方法论判断当前是否应该继续优化。

根据业务对延迟的需求,比较现在的请求延迟和gc情况,预估能接受的平均gc暂停时间,最大gc 暂停时间范围。

关掉自动balance,给一台regionserver少量增加region从而增加单机吞吐。当请求延迟超过用户要求的警戒线后,分析gc日志,找到瓶颈,优化降低gc延迟从而降低请求延迟,以便继续增加region。

当单机region数过多(可以考虑合并region),cpu负载过高,请求延迟无法降下来,任意一个条件满足,单机优化结束。稳定运行一段时间后,尝试将优化推广到整个业务分组。

增加日志


要分析gc情况一定要有gc日志。之前的日志参数如下-XX:+PrintGCDetails gc细节
-XX:+PrintGCDateStamps 时间戳
-Xloggc:${HBASE_LOG_DIR}/gc-`date +'%Y%m%d%H%M'` gc文件格式
-XX:+UseGCLogFileRotation gc文件循环
-XX:NumberOfGCLogFiles=10 文件数
-XX:GCLogFileSize=512M 文件大小
-XX:+HeapDumpOnOutOfMemoryError oom时堆dump
-XX:HeapDumpPath=${HBASE_LOG_DIR}/hbase.heapdump dump目录
-XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
-XX:+PrintAdaptiveSizePolicy 打印自适应收集的大小
-XX:+PrintFlagsFinal 打印参数值
参考其他优化的文章,增加打印参数-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间
-XX:+PrintTenuringDistribution https://www.jianshu.com/p/e634955f3bbb survivor分布情况
-XX:+PrintHeapAtGC gc前后打印堆信息
-XX:+PrintSafepointStatistics https://blog.csdn.net/u0119182 ... 47159 分析安全点统计信息,优化gc参考
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1 打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks。似乎cms更有用
gc日志太多可能会影响性能,目前没有日志对性能影响的数据,暂不考虑日志对性能的影响。


可视化

有很多gc可视化的工具。比如在线的gceasy https://gceasy.io/index.jsp#banner,上传gc日志可以进行分析。免费功能有各种图表展示。20美金一个月可以使用高级功能。

本文用gceasy做例子,其他可视化工具介绍见附录。

下面从前文优化过的节点开始,分析gc日志。








 
问题分析
gceasy优化建议

不花钱怎么变强?开启gceasy高级功能。


单次gc暂停最长到5秒了。链接给出了优化建议,见附录。

首先是优化程序降低对象创建速度。现在平均对象创建速度是1.22gb/sec。减少对象创建速度主要是更多用堆外内存,优化写放大,本文重点是gc优化,不展开。

再是年轻代太小,增大年轻代。这规则适用于其他gc,而g1 young gc是动态的,mixed gc是分次迭代的,young gc不能太大,否则young gc反而会比单次mixed慢。

再就是设置期待的最大暂停时间,目前设置100ms,暂不修改。

服务器上进程多,内存交换,服务器无这个情况。

gc线程不够多,有可能。但gc3秒以上的只有6次,差不多每天一次。如果是线程数明显不够次数应该更多,先放放。

服务器高IO导致的等待时间长。有可能,有datanode,需要进一步分析gc日志和IO监控。

没有禁用system.gc()。gc case表格来看没有相关调用。

堆太大。有可能,毕竟80GB堆。但大堆对吞吐有好处。

看5秒暂停gc的具体日志,{Heap before GC invocations=50103 (full 0):
garbage-first heap total 83886080K, used 48760053K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 5 survivors (163840K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
2019-02-18T17:48:53.570+0800: 365568.567: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 268435456 bytes, new threshold 1 (max 1)
- age 1: 87997400 bytes, 87997400 total
365568.567: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 3313913, predicted base time: 1151.36 ms, remaining time: 0.00 ms, target pause time: 100.00 ms]
365568.567: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 123 regions, survivors: 5 regions, predicted young region time: 50.17 ms]
365568.583: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 70 regions, max: 256 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
365568.583: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 70 regions, expensive: 70 regions, min: 80 regions, remaining time: 0.00 ms]
365568.583: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 123 regions, survivors: 5 regions, old: 70 regions, predicted pause time: 4353.85 ms, target pause time: 100.00 ms]
365573.866: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 40.96 %, threshold: 10.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
365573.866: [G1Ergonomics (Mixed GCs) do not continue mixed GCs, reason: reclaimable percentage not over threshold, candidate old regions: 407 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
, 5.2999017 secs]
[Parallel Time: 5227.7 ms, GC Workers: 16]
[GC Worker Start (ms): Min: 365568584.3, Avg: 365568584.3, Max: 365568584.4, Diff: 0.1]
[Ext Root Scanning (ms): Min: 1.3, Avg: 1.6, Max: 3.9, Diff: 2.7, Sum: 25.1]
[Update RS (ms): Min: 1065.2, Avg: 1067.2, Max: 1067.8, Diff: 2.6, Sum: 17075.4]
[Processed Buffers: Min: 739, Avg: 831.5, Max: 879, Diff: 140, Sum: 13304]
[Scan RS (ms): Min: 3610.8, Avg: 3611.6, Max: 3612.6, Diff: 1.8, Sum: 57786.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.3, Diff: 0.3, Sum: 0.5]
[Object Copy (ms): Min: 545.2, Avg: 546.3, Max: 547.2, Diff: 2.0, Sum: 8741.5]
[Termination (ms): Min: 0.1, Avg: 0.6, Max: 0.8, Diff: 0.7, Sum: 9.8]
[Termination Attempts: Min: 1, Avg: 465.3, Max: 552, Diff: 551, Sum: 7445]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 5227.3, Avg: 5227.4, Max: 5227.5, Diff: 0.2, Sum: 83638.8]
[GC Worker End (ms): Min: 365573811.8, Avg: 365573811.8, Max: 365573811.8, Diff: 0.0]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 3.4 ms]
[Other: 68.4 ms]
[Choose CSet: 16.3 ms]
[Ref Proc: 3.6 ms]
[Ref Enq: 0.2 ms]
[Redirty Cards: 8.7 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 31.6 ms]
[Eden: 3936.0M(3936.0M)->0.0B(3904.0M) Survivors: 160.0M->192.0M Heap: 46.5G(80.0G)->42.3G(80.0G)]
Heap after GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 44323410K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 6 young (196608K), 6 survivors (196608K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
}
[Times: user=85.30 sys=0.00, real=5.30 secs]
2019-02-18T17:48:58.871+0800: 365573.867: Total time for which application threads were stopped: 5.3131110 seconds, Stopping threads took: 0.0005771 seconds
2019-02-18T17:48:58.896+0800: 365573.893: Total time for which application threads were stopped: 0.0186168 seconds, Stopping threads took: 0.0005541 seconds
2019-02-18T17:48:58.906+0800: 365573.902: Total time for which application threads were stopped: 0.0078098 seconds, Stopping threads took: 0.0005988 seconds
2019-02-18T17:48:58.915+0800: 365573.912: Total time for which application threads were stopped: 0.0075271 seconds, Stopping threads took: 0.0003996 seconds
2019-02-18T17:48:58.923+0800: 365573.919: Total time for which application threads were stopped: 0.0072014 seconds, Stopping threads took: 0.0003114 seconds
2019-02-18T17:48:58.934+0800: 365573.930: Total time for which application threads were stopped: 0.0078706 seconds, Stopping threads took: 0.0005490 seconds
2019-02-18T17:48:58.942+0800: 365573.938: Total time for which application threads were stopped: 0.0073198 seconds, Stopping threads took: 0.0003927 seconds
2019-02-18T17:48:58.964+0800: 365573.960: Total time for which application threads were stopped: 0.0078810 seconds, Stopping threads took: 0.0007641 seconds
2019-02-18T17:48:58.972+0800: 365573.969: Total time for which application threads were stopped: 0.0075881 seconds, Stopping threads took: 0.0006277 seconds
2019-02-18T17:48:58.981+0800: 365573.978: Total time for which application threads were stopped: 0.0081290 seconds, Stopping threads took: 0.0011246 seconds
2019-02-18T17:48:58.992+0800: 365573.989: Total time for which application threads were stopped: 0.0076556 seconds, Stopping threads took: 0.0005358 seconds
2019-02-18T17:48:59.015+0800: 365574.011: Total time for which application threads were stopped: 0.0076750 seconds, Stopping threads took: 0.0005602 seconds
2019-02-18T17:48:59.026+0800: 365574.022: Total time for which application threads were stopped: 0.0089086 seconds, Stopping threads took: 0.0006000 seconds
2019-02-18T17:48:59.044+0800: 365574.041: Total time for which application threads were stopped: 0.0087554 seconds, Stopping threads took: 0.0006332 seconds
2019-02-18T17:48:59.054+0800: 365574.050: Total time for which application threads were stopped: 0.0084038 seconds, Stopping threads took: 0.0004326 seconds
{Heap before GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 48321106K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 6 survivors (196608K)
Metaspace used 50946K, capacity 51410K, committed 52168K, reserved 53248K
从日志看,期望survivor区有大概256MB,实际只有32*5=160MB,可能会将eden区直接提到old区。

近期gc时间占比过高,到40%了。

清理young 区的gc线程耗时过长,清理后可回收内存低于10%,mixed gc结束,其他gc线程空闲。说明mixed gc触发阈值设置低了,young gc参数也需要调整。

看暂停时间大于2秒的其他日志,都是触发mixed gc,但只有一个清理young和个别old区region的线程工作。确实说明mixed gc触发阈值设置低了,young gc过大了。
 
 


有41次gc的系统时间大于用户时间,大概3小时一次。

操作系统层面有可能是操作系统层异常,或者系统太忙了。出现频率大概3小时一次,看ganglia日志问题出现时cpu负载并不高。并且系统时间只是略高于用户时间不是明显有问题的数量级差距。先不考虑操作系统和JVM有bug的情况,暂不处理。

 

还是说有5秒的gc,同上。


修改参数-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UnlockExperimentalVMOptions
-XX:+ParallelRefProcEnabled
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=16
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=20
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=32m
-XX:G1MixedGCCountTarget=16
-XX:InitiatingHeapOccupancyPercent=60
-XX:MaxDirectMemorySize=110g
-XX:G1OldCSetRegionThresholdPercent=10
-XX:G1HeapWastePercent=10
-Xmx80g
-Xms80g

从gc前后堆内存图来看,-XX:InitiatingHeapOccupancyPercent=65,52GB更合适。

暂停时间平均135ms,从暂停时间图来看,young gc时间较长,需要调小young gc区,小米经验改初始值,由5改成2。-XX:G1NewSizePercent=2

mixed gc 大多数一轮不到8次,暂停时间基本都超过100ms了。期待每轮16次的参数没起作用,这是由于每轮清理的region数过多。-XX:G1OldCSetRegionThresholdPercent=5,由10改成5,减少单次mixed gc时间。

survivor区应该大于young日志中期待的,Set SurvivorRatio from 8 (default) to 4。-XX:InitiatingHeapOccupancyPercent=65
-XX:G1NewSizePercent=2
-XX:G1OldCSetRegionThresholdPercent=5
-XX:SurvivorRatio=4


设置其他必要参数

参考各类优化资料,增加以下参数-XX:+UseStringDeduplication 字符串去重,提高性能
-XX:-ResizePLAB 减少gc线程间通信的东西,关闭动态提升本地buffer
-XX:+PerfDisableSharedMem 关掉统计信息的内存映射。开启在某些特殊场景下,会极大增加gc暂停时间
优化结果




平均暂停时间,最大暂停时间,暂停时间分布,gc吞吐占比都有提高,优化成功。

接下来迭代提高这台服务器region数,做gc优化。

 
遗留问题

偶发系统时间大于用户时间的问题,依然存在。发生时系统负载并不高,有可能操作系统和JVM层面有优化空间。

gceasy建议不要用-XX:+UseGCLogFileRotation,理由是历史会看不到,文件顺序不容易分辨,多个文件不利于gc分析。但这个参数意义是控制总文件大小,所以依然保留。

附录
g1官方介绍

https://www.oracle.com/technet ... .html

其他可视化工具介绍

https://blog.csdn.net/qq_32447 ... 67984

https://blog.csdn.net/xuelinme ... 90115

https://blog.csdn.net/renfufei ... 78064

gceasy优化建议

https://blog.gceasy.io/2016/11 ... uses/

https://blog.gceasy.io/2016/12 ... time/

https://blog.gceasy.io/2016/12 ... pped/

https://blog.gceasy.io/2018/12 ... tion/

https://blog.gceasy.io/2019/01 ... tion/

优化参考

https://www.oracle.com/technet ... .html

https://blogs.apache.org/hbase ... hbase

https://software.intel.com/en- ... hbase

http://openinx.github.io/ppt/h ... 8.pdf

https://blog.csdn.net/songhaif ... 77612

https://blog.csdn.net/maosijun ... 62489

https://dzone.com/articles/g1g ... ags-1

http://www.evanjones.ca/jvm-mmap-pause.html

之前的优化探索
 http://www.hbase.group/article/192
 
专栏地址
 
https://zhuanlan.zhihu.com/c_178811296 查看全部
优化的最终目的是保障现有用户体验的同时,减少机器,节约成本。

为了更好的编写本文和后续文章,花费20美金。欢迎去附录链接专栏赞赏支持。

g1介绍
g1特点


g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。

g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在预期范围内。

g1适用场景

对平均响应时间,最大响应时间有严格要求的应用系统,如hbase regionserver。

优化原则
先优化业务层和应用层


系统调优是从业务到实现,从整体到局部,从架构到具体组件的。在进行gc调优之前,我们应该确保业务层和应用层已经评估优化过。业务层和应用层的优化一般来说更容易有收益,我们不能指望一个架构设计有缺陷,应用层代码有很多已知问题的系统,通过gc调优一劳永逸。

gc调优3选2原则

先来看一下衡量gc的指标有哪些。对应用吞吐量的影响(一般是gc对cpu的消耗),对延迟的影响,总内存占用(gc触发时留有内存业务可以继续,留有内存做对象拷贝碎片整理等操作,不能oom)。

GC调优3选2原则: 在吞吐量、延迟、内存占用上,我们只能选择其中两个进行调优,无法三者兼得。

hbase已有业务regionserver的调优目标

在调优之前,必须要有明确的性能优化目标, 然后找到未达到该目标的性能瓶颈。再针对瓶颈做优化。通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

hbase集群启用了group分组,重要业务有独立的regionserver分组。

重要业务regionserver的调优目标是,在满足业务延迟要求的基础上,用尽量低的成本,满足业务吞吐量的峰值需求。

也就是说,总吞吐量固定,延迟要求固定,单机cpu和内存固定,求最小机器数。

再转换一下,对单机来说,延迟指标确定,将单机吞吐在单机cpu和内存允许的范围内调整到最大。

需要说明的是,单机能承担多少吞吐,跟业务访问模型,region数,读写缓存参数,网络IO,磁盘IO都有关系。业务和hbase参数的调整应该在gc优化之前进行,网络和磁盘IO一般是应用层优化的。所以下文假设业务层和应用层已优化完毕,网络和磁盘都不是瓶颈,只聚焦在gc参数调优。

本文假设我们换算后的延迟目标是平均gc暂停时间100ms,最大暂停时间2s,gc时间占比3%以内。实际达到这个目标后,还要通过regionserver监控确定请求的延时要是否在用户用户的要求范围内。

影响延迟的因素

gc的时间占比。平均stw gc时间,频率。毛刺stw gc时间,频率。峰值stw gc时间,频率。

一般来说,regionserver应该避免full gc。

新生代越大,单次young gc时间越长,频率越低。

mixed gc受gc触发时机,gc并发线程数,预期迭代次数,每个迭代回收分片比例等多个参数影响,详见附录官方文档。

关于JVM版本

目前生产环境用1.8.0_77, 小米hbase环境用1.8.0_111, Oracle jdk的8最新版本是8u201。

intel性能测试见附录,jdk7不同版本间g1性能差距很大。Jdk7u21升级到jdk7u60,gc以及stw gc的平均时间,最大时间,时间分布都有大幅优化。

所以应该尽量用最新版本的JDK。

优化步骤

需要有方法论判断当前是否应该继续优化。

根据业务对延迟的需求,比较现在的请求延迟和gc情况,预估能接受的平均gc暂停时间,最大gc 暂停时间范围。

关掉自动balance,给一台regionserver少量增加region从而增加单机吞吐。当请求延迟超过用户要求的警戒线后,分析gc日志,找到瓶颈,优化降低gc延迟从而降低请求延迟,以便继续增加region。

当单机region数过多(可以考虑合并region),cpu负载过高,请求延迟无法降下来,任意一个条件满足,单机优化结束。稳定运行一段时间后,尝试将优化推广到整个业务分组。

增加日志


要分析gc情况一定要有gc日志。之前的日志参数如下
-XX:+PrintGCDetails gc细节
-XX:+PrintGCDateStamps 时间戳
-Xloggc:${HBASE_LOG_DIR}/gc-`date +'%Y%m%d%H%M'` gc文件格式
-XX:+UseGCLogFileRotation gc文件循环
-XX:NumberOfGCLogFiles=10 文件数
-XX:GCLogFileSize=512M 文件大小
-XX:+HeapDumpOnOutOfMemoryError oom时堆dump
-XX:HeapDumpPath=${HBASE_LOG_DIR}/hbase.heapdump dump目录
-XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
-XX:+PrintAdaptiveSizePolicy 打印自适应收集的大小
-XX:+PrintFlagsFinal 打印参数值

参考其他优化的文章,增加打印参数
-XX:+PrintGCApplicationStoppedTime    打印垃圾回收期间程序暂停的时间
-XX:+PrintTenuringDistribution https://www.jianshu.com/p/e634955f3bbb survivor分布情况
-XX:+PrintHeapAtGC gc前后打印堆信息
-XX:+PrintSafepointStatistics https://blog.csdn.net/u0119182 ... 47159 分析安全点统计信息,优化gc参考
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1 打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks。似乎cms更有用

gc日志太多可能会影响性能,目前没有日志对性能影响的数据,暂不考虑日志对性能的影响。


可视化

有很多gc可视化的工具。比如在线的gceasy https://gceasy.io/index.jsp#banner,上传gc日志可以进行分析。免费功能有各种图表展示。20美金一个月可以使用高级功能。

本文用gceasy做例子,其他可视化工具介绍见附录。

下面从前文优化过的节点开始,分析gc日志。








 
问题分析
gceasy优化建议


不花钱怎么变强?开启gceasy高级功能。


单次gc暂停最长到5秒了。链接给出了优化建议,见附录。

首先是优化程序降低对象创建速度。现在平均对象创建速度是1.22gb/sec。减少对象创建速度主要是更多用堆外内存,优化写放大,本文重点是gc优化,不展开。

再是年轻代太小,增大年轻代。这规则适用于其他gc,而g1 young gc是动态的,mixed gc是分次迭代的,young gc不能太大,否则young gc反而会比单次mixed慢。

再就是设置期待的最大暂停时间,目前设置100ms,暂不修改。

服务器上进程多,内存交换,服务器无这个情况。

gc线程不够多,有可能。但gc3秒以上的只有6次,差不多每天一次。如果是线程数明显不够次数应该更多,先放放。

服务器高IO导致的等待时间长。有可能,有datanode,需要进一步分析gc日志和IO监控。

没有禁用system.gc()。gc case表格来看没有相关调用。

堆太大。有可能,毕竟80GB堆。但大堆对吞吐有好处。

看5秒暂停gc的具体日志,
{Heap before GC invocations=50103 (full 0):
garbage-first heap total 83886080K, used 48760053K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 5 survivors (163840K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
2019-02-18T17:48:53.570+0800: 365568.567: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 268435456 bytes, new threshold 1 (max 1)
- age 1: 87997400 bytes, 87997400 total
365568.567: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 3313913, predicted base time: 1151.36 ms, remaining time: 0.00 ms, target pause time: 100.00 ms]
365568.567: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 123 regions, survivors: 5 regions, predicted young region time: 50.17 ms]
365568.583: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 70 regions, max: 256 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
365568.583: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 70 regions, expensive: 70 regions, min: 80 regions, remaining time: 0.00 ms]
365568.583: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 123 regions, survivors: 5 regions, old: 70 regions, predicted pause time: 4353.85 ms, target pause time: 100.00 ms]
365573.866: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 40.96 %, threshold: 10.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
365573.866: [G1Ergonomics (Mixed GCs) do not continue mixed GCs, reason: reclaimable percentage not over threshold, candidate old regions: 407 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
, 5.2999017 secs]
[Parallel Time: 5227.7 ms, GC Workers: 16]
[GC Worker Start (ms): Min: 365568584.3, Avg: 365568584.3, Max: 365568584.4, Diff: 0.1]
[Ext Root Scanning (ms): Min: 1.3, Avg: 1.6, Max: 3.9, Diff: 2.7, Sum: 25.1]
[Update RS (ms): Min: 1065.2, Avg: 1067.2, Max: 1067.8, Diff: 2.6, Sum: 17075.4]
[Processed Buffers: Min: 739, Avg: 831.5, Max: 879, Diff: 140, Sum: 13304]
[Scan RS (ms): Min: 3610.8, Avg: 3611.6, Max: 3612.6, Diff: 1.8, Sum: 57786.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.3, Diff: 0.3, Sum: 0.5]
[Object Copy (ms): Min: 545.2, Avg: 546.3, Max: 547.2, Diff: 2.0, Sum: 8741.5]
[Termination (ms): Min: 0.1, Avg: 0.6, Max: 0.8, Diff: 0.7, Sum: 9.8]
[Termination Attempts: Min: 1, Avg: 465.3, Max: 552, Diff: 551, Sum: 7445]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 5227.3, Avg: 5227.4, Max: 5227.5, Diff: 0.2, Sum: 83638.8]
[GC Worker End (ms): Min: 365573811.8, Avg: 365573811.8, Max: 365573811.8, Diff: 0.0]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 3.4 ms]
[Other: 68.4 ms]
[Choose CSet: 16.3 ms]
[Ref Proc: 3.6 ms]
[Ref Enq: 0.2 ms]
[Redirty Cards: 8.7 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 31.6 ms]
[Eden: 3936.0M(3936.0M)->0.0B(3904.0M) Survivors: 160.0M->192.0M Heap: 46.5G(80.0G)->42.3G(80.0G)]
Heap after GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 44323410K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 6 young (196608K), 6 survivors (196608K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
}
[Times: user=85.30 sys=0.00, real=5.30 secs]
2019-02-18T17:48:58.871+0800: 365573.867: Total time for which application threads were stopped: 5.3131110 seconds, Stopping threads took: 0.0005771 seconds
2019-02-18T17:48:58.896+0800: 365573.893: Total time for which application threads were stopped: 0.0186168 seconds, Stopping threads took: 0.0005541 seconds
2019-02-18T17:48:58.906+0800: 365573.902: Total time for which application threads were stopped: 0.0078098 seconds, Stopping threads took: 0.0005988 seconds
2019-02-18T17:48:58.915+0800: 365573.912: Total time for which application threads were stopped: 0.0075271 seconds, Stopping threads took: 0.0003996 seconds
2019-02-18T17:48:58.923+0800: 365573.919: Total time for which application threads were stopped: 0.0072014 seconds, Stopping threads took: 0.0003114 seconds
2019-02-18T17:48:58.934+0800: 365573.930: Total time for which application threads were stopped: 0.0078706 seconds, Stopping threads took: 0.0005490 seconds
2019-02-18T17:48:58.942+0800: 365573.938: Total time for which application threads were stopped: 0.0073198 seconds, Stopping threads took: 0.0003927 seconds
2019-02-18T17:48:58.964+0800: 365573.960: Total time for which application threads were stopped: 0.0078810 seconds, Stopping threads took: 0.0007641 seconds
2019-02-18T17:48:58.972+0800: 365573.969: Total time for which application threads were stopped: 0.0075881 seconds, Stopping threads took: 0.0006277 seconds
2019-02-18T17:48:58.981+0800: 365573.978: Total time for which application threads were stopped: 0.0081290 seconds, Stopping threads took: 0.0011246 seconds
2019-02-18T17:48:58.992+0800: 365573.989: Total time for which application threads were stopped: 0.0076556 seconds, Stopping threads took: 0.0005358 seconds
2019-02-18T17:48:59.015+0800: 365574.011: Total time for which application threads were stopped: 0.0076750 seconds, Stopping threads took: 0.0005602 seconds
2019-02-18T17:48:59.026+0800: 365574.022: Total time for which application threads were stopped: 0.0089086 seconds, Stopping threads took: 0.0006000 seconds
2019-02-18T17:48:59.044+0800: 365574.041: Total time for which application threads were stopped: 0.0087554 seconds, Stopping threads took: 0.0006332 seconds
2019-02-18T17:48:59.054+0800: 365574.050: Total time for which application threads were stopped: 0.0084038 seconds, Stopping threads took: 0.0004326 seconds
{Heap before GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 48321106K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 6 survivors (196608K)
Metaspace used 50946K, capacity 51410K, committed 52168K, reserved 53248K

从日志看,期望survivor区有大概256MB,实际只有32*5=160MB,可能会将eden区直接提到old区。

近期gc时间占比过高,到40%了。

清理young 区的gc线程耗时过长,清理后可回收内存低于10%,mixed gc结束,其他gc线程空闲。说明mixed gc触发阈值设置低了,young gc参数也需要调整。

看暂停时间大于2秒的其他日志,都是触发mixed gc,但只有一个清理young和个别old区region的线程工作。确实说明mixed gc触发阈值设置低了,young gc过大了。
 
 


有41次gc的系统时间大于用户时间,大概3小时一次。

操作系统层面有可能是操作系统层异常,或者系统太忙了。出现频率大概3小时一次,看ganglia日志问题出现时cpu负载并不高。并且系统时间只是略高于用户时间不是明显有问题的数量级差距。先不考虑操作系统和JVM有bug的情况,暂不处理。

 

还是说有5秒的gc,同上。


修改参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UnlockExperimentalVMOptions
-XX:+ParallelRefProcEnabled
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=16
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=20
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=32m
-XX:G1MixedGCCountTarget=16
-XX:InitiatingHeapOccupancyPercent=60
-XX:MaxDirectMemorySize=110g
-XX:G1OldCSetRegionThresholdPercent=10
-XX:G1HeapWastePercent=10
-Xmx80g
-Xms80g


从gc前后堆内存图来看,-XX:InitiatingHeapOccupancyPercent=65,52GB更合适。

暂停时间平均135ms,从暂停时间图来看,young gc时间较长,需要调小young gc区,小米经验改初始值,由5改成2。-XX:G1NewSizePercent=2

mixed gc 大多数一轮不到8次,暂停时间基本都超过100ms了。期待每轮16次的参数没起作用,这是由于每轮清理的region数过多。-XX:G1OldCSetRegionThresholdPercent=5,由10改成5,减少单次mixed gc时间。

survivor区应该大于young日志中期待的,Set SurvivorRatio from 8 (default) to 4。
-XX:InitiatingHeapOccupancyPercent=65
-XX:G1NewSizePercent=2
-XX:G1OldCSetRegionThresholdPercent=5
-XX:SurvivorRatio=4



设置其他必要参数

参考各类优化资料,增加以下参数
-XX:+UseStringDeduplication 字符串去重,提高性能
-XX:-ResizePLAB 减少gc线程间通信的东西,关闭动态提升本地buffer
-XX:+PerfDisableSharedMem 关掉统计信息的内存映射。开启在某些特殊场景下,会极大增加gc暂停时间

优化结果




平均暂停时间,最大暂停时间,暂停时间分布,gc吞吐占比都有提高,优化成功。

接下来迭代提高这台服务器region数,做gc优化。

 
遗留问题

偶发系统时间大于用户时间的问题,依然存在。发生时系统负载并不高,有可能操作系统和JVM层面有优化空间。

gceasy建议不要用-XX:+UseGCLogFileRotation,理由是历史会看不到,文件顺序不容易分辨,多个文件不利于gc分析。但这个参数意义是控制总文件大小,所以依然保留。

附录
g1官方介绍


https://www.oracle.com/technet ... .html

其他可视化工具介绍

https://blog.csdn.net/qq_32447 ... 67984

https://blog.csdn.net/xuelinme ... 90115

https://blog.csdn.net/renfufei ... 78064

gceasy优化建议

https://blog.gceasy.io/2016/11 ... uses/

https://blog.gceasy.io/2016/12 ... time/

https://blog.gceasy.io/2016/12 ... pped/

https://blog.gceasy.io/2018/12 ... tion/

https://blog.gceasy.io/2019/01 ... tion/

优化参考

https://www.oracle.com/technet ... .html

https://blogs.apache.org/hbase ... hbase

https://software.intel.com/en- ... hbase

http://openinx.github.io/ppt/h ... 8.pdf

https://blog.csdn.net/songhaif ... 77612

https://blog.csdn.net/maosijun ... 62489

https://dzone.com/articles/g1g ... ags-1

http://www.evanjones.ca/jvm-mmap-pause.html

之前的优化探索
 http://www.hbase.group/article/192
 
专栏地址
 
https://zhuanlan.zhihu.com/c_178811296

hbase coprocessor preput方法没有触发

hbasewukaishanda08 回复了问题 • 2 人关注 • 2 个回复 • 358 次浏览 • 2019-02-28 18:24 • 来自相关话题

条新动态, 点击查看
smartZY

smartZY 回答了问题 • 2018-07-13 10:43 • 2 个回复 不感兴趣

hbase 删表过程是怎么样的

赞同来自:

会把所有数据放到archive目录下,后台有线程周期去检查这些数据可不可以删,周期一般是5分钟。如果这张表之前有快照,那么数据不可删,和major compaction没有关系。
会把所有数据放到archive目录下,后台有线程周期去检查这些数据可不可以删,周期一般是5分钟。如果这张表之前有快照,那么数据不可删,和major compaction没有关系。
hbasegroup

hbasegroup 回答了问题 • 2018-08-10 10:48 • 3 个回复 不感兴趣

如何从solr和hbase中删除所有数据

赞同来自:

如果你想从 Solr 中删除数据,可以通过 URL 方式实现:


[url=http://host:port/solr/[core name]/update?stream.body=<delete><query>*:*</que... 显示全部 »
如果你想从 Solr 中删除数据,可以通过 URL 方式实现:


[url=http://host:port/solr/[core name]/update?stream.body=<delete><query>*:*</query></delete>&commit=true ]http://host:port/solr/ /update?stream.body=*:*&commit=true

把上面的 [core name] 替换成你的 core 名字即可。

<delete><query>*:*</query></delete>
 
这个就是删除所有数据。记得 url 里面要带 commit=true,其含义是让这个修改生效。
 
删除 HBase 表可以先 disable 表,然后 delete 表。
hbasegroup

hbasegroup 回答了问题 • 2018-08-10 11:29 • 9 个回复 不感兴趣

如何快速统计 HBase 里面有多少行数据

赞同来自:

可以试试 HBase 里面的 RowCounter 。RowCounter 内部是使用 MapReduce 来计算一个表里面的行数。使用方法如下:[code]$ hbase org.apache.hadoop.hbase.mapreduce.RowCounte... 显示全部 »
可以试试 HBase 里面的 RowCounter 。RowCounter 内部是使用 MapReduce 来计算一个表里面的行数。使用方法如下:[code]$ hbase org.apache.hadoop.hbase.mapreduce.RowCounter <tablename>

Usage: RowCounter [options]
<tablename> [
--starttime=[start]
--endtime=[end]
[--range=[startKey],[endKey]]

]
cloudera manager version: 5.13.3
hbase version:HBase 1.2.0-cdh5.13.3
运行任务:bulkload
下面所有的路径都是在hdfs上前几天我也遇到过这样的问题:disable 'tablename... 显示全部 »
cloudera manager version: 5.13.3
hbase version:HBase 1.2.0-cdh5.13.3
运行任务:bulkload
下面所有的路径都是在hdfs上前几天我也遇到过这样的问题:disable 'tablename' 成功;drop 'tablename',卡住,运行一段时间后,然后提示:The procedure 452 is still running;
后经过测试发现:
1,在hbase的命令行里,建表,插入数据,禁用表,删除表都是成功的;
2,只有经过bulkload的导入的数据,在drop 'tablename',时会提示The procedure 【号】 is still running;
后来又从网上找资料,测试等等,终于发现原因
最主要的原因是:【权限问题】
1,任务运行统一在普通用户目录下,比如说edq
2,从关系型数据库导出数据到hdfs中去,文件的权限是:  
文件权限       所属用户       所属组
-rw-r--r--     edq               hive
3,bulkload,其实有两步:
1)将源文件生成hfile文件,文件权限和上面一样:
文件权限       所属用户       所属组
-rw-r--r--     edq               hive
2)将hfile文件load进hbase中去,文件权限和上面一样:
文件权限       所属用户       所属组
-rw-r--r--     edq               hive
4,想必各位看官,有点明白了,我接着说,那我们看看hdfs上hbase中【/hbase/data/default/tablename/.../...】的文件权限。
hbase中的文件权限是:
文件权限       所属用户       所属组
-rw-r--r--     hbase              hbase
hbase中的文件所属用户统一为hbase用户
5,当我们想要drop删除hbase 表的时候,
表的hfile文件会从/hbase/data/default/tablename/../..移到/hbase/.tmp/data/default/tablename/...中,此时,当hbase想要删除
/hbase/.tmp/data/default/tablename/.. 表时,发现没有权限,为啥,因为
我的hfile文件权限为:
文件权限       所属用户       所属组
-rw-r--r--     edq               hive
所属者是edq,其他人只有r--,read权限,所以hbase用户无法删除该文件,如何验证,当你重启hbase时,会启动失败,因为,会报权限问题,无法删除临时目录下的文件:/hbase/.tmp/data/default/tablename/.. 
6,该怎么做?
进行删除操作之前赋予权限,我的是在bulkload第二步之前,进行赋予权限,将文件other用户添加为w权限,hbase即可删除:
hdfs dfs -chmod -R o+w   /hfilepath
7,希望对遇到此问题的朋友有所帮助
 
 
 
 
 
 
 
 
 
beyond

beyond 回答了问题 • 2018-09-10 09:53 • 8 个回复 不感兴趣

HBase面试题目答案集锦

赞同来自:

有好的问题或者分享也可以在论坛里分享出来
有好的问题或者分享也可以在论坛里分享出来
最新的 HBase 3.0.0-SNAPSHOT 也只是支持 Hadoop 3.0.3,你这个 Hadoop 版本太新了吧。
 
你使用 HBase 2.1.0 最新版把里面的 <hadoop-three.version>3.0.3</had... 显示全部 »
最新的 HBase 3.0.0-SNAPSHOT 也只是支持 Hadoop 3.0.3,你这个 Hadoop 版本太新了吧。
 
你使用 HBase 2.1.0 最新版把里面的 <hadoop-three.version>3.0.3</hadoop-three.version> 修改成 <hadoop-three.version>3.1.1</hadoop-three.version> 看能不能编译通过。
beyond

beyond 回答了问题 • 2018-09-21 12:55 • 1 个回复 不感兴趣

hbase建表分区的问题

赞同来自:

经过社区同学的帮助,这个问题已经解决;
(1)在建表时,若不进行预分区,则默认的splitKeys为null,即只有一个分区。此时的startKey和endKey都为null,只有region大小达到阈值是才会进行split(除手动分区),入下图所示 
(2)... 显示全部 »
经过社区同学的帮助,这个问题已经解决;
(1)在建表时,若不进行预分区,则默认的splitKeys为null,即只有一个分区。此时的startKey和endKey都为null,只有region大小达到阈值是才会进行split(除手动分区),入下图所示 
(2)在建表时,若进行预分区,则此时的分区数目至少为3,并且startKey和endKey不能相同,否则会报:IllegalArgumentException("Start key must be smaller than end key");这个异常。
当region数为3的时候,调用createTable(desc, new byte{startKey, endKey});,此时的startKey,和endKey即为分区的splitKeys,按照下图的方法进行分区

273
 
当region数大于3的时候,调用split(startKey, endKey, numRegions - 3)方法获得splitKeys,如下图所示,此时的split是在sartKey和endKey进行均衡分区 ,根据split获取splitKeys,然后根据splitKeys进行分区
274
 

hbase执行手动大合并的时候,执行了一周还是没有结束,是怎么回事?

回复

hbase果农 发起了问题 • 1 人关注 • 0 个回复 • 252 次浏览 • 2019-04-22 08:50 • 来自相关话题

hbase优化之旅(四)-regionserver从17优化到10台的小结

hbasechuxiao 发表了文章 • 3 个评论 • 623 次浏览 • 2019-03-25 18:47 • 来自相关话题

本文是前几篇文章的小结,介绍如何通过技术手段,挖掘hbase服务的潜力,将分组的服务器数从17台降低到10台。
首发于专栏 :https://zhuanlan.zhihu.com/p/60357239 

目录
确定优化目标
系统资源瓶颈和优化效果
cpu利用率
内存使用
网络IO
磁盘IO
存储量
稳定性
综述
优化细节
合并region
hbase参数调整
未来的工作
近期工作
中远期工作

确定优化目标
沟通交流后,业务方更看重降低成本。数据量梳理后略有降低,保证吞吐,无长期请求堆积前提下可以放宽延时要求。为了更快的进行优化,放宽稳定性可以要求接受短期波动。
另外,该分组的regionserver之前存在不稳定的问题,这次优化也一并解决。

系统资源瓶颈和优化效果
降低成本,即省机器,就是用更少的服务器满足业务需求。需要搞清楚单台服务器的瓶颈在哪。下面分析单台服务器的指标和优化效果。
cpu利用率
因为hbase是重IO,轻计算的服务,一般来说hbase服务cpu利用率较低。优化前单台cpu利用率平均在5%,峰值在10%左右。直接机器减半也没有压力。
空闲的cpu利用率,可以用来置换更高的存储压缩比,节约存储空间。更多的业务线程和gc线程,提高吞吐能力,减少gc对系统吞吐和延时毛刺的影响。
峰值cpu在40%以下,说明cpu资源充足富裕,可以减机器,用cpu换存储空间,提高业务吞吐。


内存使用
256GB内存,datanode 4g,regionserver堆内50g,堆外100g,空闲大概100g。可以提高单机regionserver内存到堆内100g,堆外120g,留10%的空闲。
提高内存的好处很多。单机负载不变的前提下,增加堆内内存,可以降低gc频率减少毛刺出现频率,降低gc时间占比提高吞吐(另外如果mixed gc周期能超过大多数region flush的周期,回收会更有效率)。增加memstore写缓存,可以降低写放大效应,从而降低网络和磁盘IO。降低写放大,还可以减少compact,提高缓存有效性从而增加缓存命中率,进一步降低读IO,减少读请求延时。增加堆外内存,可以缓存命中率。
举个例子,一台服务器提高内存使用量,同时region数变为其他服务器的2倍。堆外内存增加20%而region数翻倍,单纯数字上来看缓存命令率应该下降,实际稳定后命中率保持在96%高于其他服务器的94%,这就是降低compact提高命中率的收益。
增大内存的坏处是,单次gc时间会更长,极端情况下请求延时的毛刺会更明显。另外一些hbase内部的数据结构,如memstore的ConcurrentSkipListMap,对象数太多性能会下降的很厉害,需要结合用户需求,在region数,hbase-site参数,gc参数上综合考虑,做一个权衡。


网络IO
网络In 20MB,Out 40到100MB波动。万兆网卡峰值也就百MB以内,机器数砍半没压力。

磁盘IO
非major compact时段,磁盘读流量较高,高峰期200MB到300MB,主要来自compact。写流量50MB/s。磁盘流量较高是个瓶颈,需要技术优化。
磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5左右),如果不考虑服务端filter过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
用户的业务场景没用filter,所以可以推论读IO主要来自compact ,验证了写放大效应明显。
经过参数优化降低写放大效应,region数不变,单机磁盘读IO下降到之前的1/3,80MB左右。
又经过region合并,参数继续优化,单机两倍region负载,读Io保持在150MB到200MB之间,比之前单倍region负载还要低很多。

存储量
集群存储峰值大概有三个时间点,每周major compact前,每周major compact后,major compact中(大概是major compact前存储*(1+并发major region数/总region数))。
目前没有明确用哪个存储量衡量峰值,一般定期检查,发现用到差不多了就和用户沟通加机器。不允许独立分组存储总量大于分组机器所提供的存储总量。
在major compact前取了数据的总量,刚好跟10台的存储量差不多,可以降低到10台。

稳定性
如系列文章上一篇gc执行细节和参数调优方法论详所说,分组之前存在稳定性的问题。出现问题时需要人工操作迁移region,重启。当机器数减到17台,直接减机器立刻加剧了问题出现的频率。经分析,问题主要出在gc参数上,详见上一篇文章。

综述
通过瓶颈分析,解决了磁盘IO问题后,10台服务器是存储量瓶颈,所以第一步的目标是降到10台。



优化细节
合并region
之前分组17台,单机region数大概500上下,region数太多导致flush都是小文件,写放大罪魁祸首。合并小于10g 的region,将region数降低到单机130+,写放大效应立刻降低了,可以减少机器数了。
一个批量写请求,regionserver内写多个region是串行的,这个角度减少region数可以改善写延时。同时单个region memstore太大,ConcurrentSkipListMap的结构插入性能会降低。目前没发现减少region后明显的写延时差别。杭州的同事分享过阿里对memstore数据结构的优化,版本升级到1.4.8后可以评估能否用的到。
另外,大region会导致major compact压力更大。这个可以通过修改compact policy来解决。

hbase参数调整
增大内存
堆内存 50g->100g
堆外内存 100g->120g
大内存降低写放大效应,提高读缓存命中率,支持更高的吞吐能力。改善了gc表现。


基础配置
hbase.regionserver.handler.count 192->384
hbase.ipc.server.callqueue.read.share 无->0.4

hbase.ipc.server.callqueue.handler.factor 无->0.2
两倍region后,监控看当前handler经常达到192上限,按其他公司经验调大一倍。有时能打到上限。
线程多可以减少队列等待时间,可能增加请求处理时间,监控看,调大能显著减少队列等待时间p99,请求时间p99没有明显变化。
读写分离主要是能降低p99,避免慢写入/高并发scan等耗时长的操作堵塞句柄。192个句柄时,等待队列的p99有时会持续在500ms-1s左右。调大到384,增加读写分离,等待队列p99就维持在100ms以下了。
5个句柄使用一个队列,是为了降低高并发时的锁资源争抢。需要权衡队列比例,如果1,2个句柄就使用一个队列,很容易几个慢请求就把队列里的后续请求都堵住了。目前设置为5:1.

memstrore
hbase.hstore.flusher.count 15-->8 flush线程数
base.regionserver.optionalcacheflushinterval 无->7200000 (1小时到2小时)
hbase.regionserver.hlog.blocksize 无 -> 268435456 (实际值没变,由使用hdfs块大小变为显式设置)
hbase.regionserver.maxlogs 52 -> 200

除了regionserver重启时memstore flush外,有几种情况会触发memstore flush,分别是单个region的memstore达到上限,单个region的memstore距离上次flush过了刷新周期,hlogs达到上限flush涉及的还未flush的region,总memstore达到内存设置上限。这几种情况按照从好到差顺序排列,越靠后对系统的稳定性影响越高,应尽量避免。

堆内存的增加,直接增大了memstore内存上限,增大了单region flush的容量,可以刷大文件减少写放大效应。同时尽量让region写满128MB再flush,可以错开flush时间,错开compact时间,降低磁盘IO峰值,减少flush和compact排队现象。
10台均分region,调整后单台的memstore在10g到30g波动。为了尽量128MB在刷,其他几种flush方式的参数要跟着调整。

虽然我们要避免后面几种flush情况出现,但当业务突然有写入热点,或机器重启各region的memstore重置,可能会触发定时刷新/达到hlogs上限引起集中flush。为降低同时flush并发高引起的问题,降低了并发flush数。由于memstore足够大,单次flush周期长,即使控制flush并发,也不会充暴memstore内存造成写堵塞。
此外,控制flush数可以间接控制minor compact的压力。

按10台regionserver计算规模和请求量,白天绝大多数region在2个内memstore达到128MB,2小时刷新可以确保绝大多数region写满128MB自动刷新,减少写放大。如果按默认1小时,重启后的前2,3天里,会有很多region在相同时间flush,触发compact,系统压力很大。2,3天后,靠着刷新时间每次的随机波动才能慢慢分散开。所以应确保白天峰值写入量时,大多数region都能在刷新周期里写满flush。

hlogs过少会造成刷新周期太短。以之前经常延时变长的一台服务器为例,平均5到10分钟强刷一次,刷的都是小文件,带来了严重的读写放大后果。之前region数三倍于现在,和gc参数不合适一起造成了偶发的gc时间占比高影响业务的问题。 另外,目前hlogs 50的配置会造成同时flush大量region,同时compact,系统压力大,造成请求和吞吐的毛刺。
maxlogs的配置多大合适?maxlogs 从90,到120,150,200,250,300,350,400都实验过,越大的hlogs对缓解region写入不均,调大flush尺寸和周期越有利 。
单纯按照流传的公式来看, 下限 50 * 1024/(256*0.95)= 210,上限60 * 1024/(256*0.95)= 252,应该在210到252之间。实际由于各region会陆续触发flush,hlogs即使到达252,memstore总内存可能只有10-20g,依然可以调大。
hlogs上限提高能解决重启后同时flush密集的问题。重启后,各region memstore都是从0开始缓存,hlogs到上限时如果大量region没写满,会触发大量region同时 flush,这些region的memstore再次清零了,下个周期依然有大量region同时flush,打散速度慢于刷新周期触发的flush。hlogs上限出发的同时flush,compact对gc压力很大,请求延时会周期性显著提升。如果256MB flush,8台服务器,hlogs需要到400。128MB则200即可。
hlogs过多可能有什么影响?1.重启时间变长。重试时间长本质是总memstore增大,flush时间长造成的。之前17台规模单台重启大概1分钟,8台规模单台大概1分50秒,总时间并没有增加。再就是如果某台regionserver挂掉,集群要重读未flush的hlogs,hlogs多了会增加重读的量,增加集群负担。

综上所述,最终flush size定为128MB,hlogs上限定为200。

读cache
hfile.block.cache.size 0.19 ->0.2
hbase.bucketcache.size 102400 -> 132000
hbase.bucketcache.percentage.in.combinedcache 0.9 -> 0.85

缓存大小配合堆内堆外内存增加的调整,缓存变成堆外上限112g,堆内20g。L1和L2的比例按实际线上情况由0.9调整为0.85.


compact
hbase.regionserver.thread.compaction.small 6 -> 8
hbase.regionserver.thread.compaction.large 3 -> 4

略微提高minor compact 和major compact速度,尤其是major compact速度,以便机器减少到一半时,夜里能major完。调到12,gc压力过大,所以只是微调。

hdfs
dfs.client.hedged.read.threadpool.size 50 ->300 
dfs.client.hedged.read.threshold.millis 150->500
hbase.regionserver.hlog.slowsync.ms 无->400
compact负载一高,200线程池会报大量的线程池满,资源不够用,所以调到300。
我们用多路读是为了当磁盘故障时,可以读其他副本。如果超时时间太低,可以读本地的去读了远程副本,显著增大集群网络和磁盘IO。读包括compact的读,是轻延时重吞吐的,集群磁盘IO负载高,延时增加,触发多路读又增大了集群的IO压力。尤其是本地化不是100%时,会读其他机器上的副本,400毫秒也依然容易超时,所以超时时间改为500,确保在一般的高负载和非本地化场景中,也不会给集群额外的压力,只有磁盘真正故障堵塞读的时候再读其他副本上。由于95%以上的读都来自内存缓存,500毫秒的最大超时时间并不会造成显著的读请求延时升高,毕竟常态的gc也要几百ms时间。
负载稍微高点,日志文件满屏都是wal log slow,淹没了其他需要关注的问题。由于wal是单线程顺序写单文件,写入速度是有瓶颈的,调到400,只有负载较高时才会打印。

gc参数调整
请看上一篇,本文不再叙述。

未来的工作
近期工作
打散major compact执行时间
现在集群内所有业务分组同一天晚上进行major compact ,集群网络和磁盘IO压力大,存储量也会同时飙升。major compact即将打散到每周7天的晚上,以降低集群的压力。

换compact policy
compact policy优化,需要用户配合做客户端代码优化,和业务方暂定4月初共建。
现在所有业务用的都是默认的default policy,中规中矩。而该项目业务场景非常适合用Date Tiered Compaction,该策略能极大降低compact写放大效应尤其是major compact的压力,并且提升近期热点数据查询速度。
更换该策略,需要业务方略微修改客户端代码,读写时增加TTL的设置,否则可能会降低查询速度。

换压缩算法
换高压缩比算法gz能进一步减少存储总量,已有经验是可以降低30%左右。即存储量视角,可以降到7台服务器规模。换压缩算法会增加cpu利用率,可能对用户读写造成未知的影响,待4月和用户共建。


中远期工作
2副本
好处是存储量立刻降低1/3,坏处是集群同时坏2块盘的概率比坏3块高得多,更容易丢数据。一般是偏离线,稳定性要求不高,存储量偏高的业务,数据在hive有一份,即使有数据缺失可以很快从hive导一份。需要和用户进一步确定业务模式是否能采用。

超卖
目前成本分摊策略,独立业务分组按regionserver服务器数分摊成本,如果独立业务分组存储总量大于分组机器所提供的存储总量,即认为存储量达到瓶颈。就必须要增加机器,不同的业务瓶颈不一样,按存储量严格限制,不利于提升集群资源的整体利用率。
所以从整个集群的资源利用率来看,允许部分吞吐,延时要求不高的业务使用更多的存储,即存储超卖,可以更有效提高集群资源利用率,释放数据引擎潜力,降低业务方使用成本,为公司省钱。
超卖涉及三个维度。超卖存储每TB价格,超卖存储量如何计算。超卖比例,
现在每TB成本是整机打包折算的,而超卖的存储只涉及纯磁盘的折算成本。要超卖存储,需要额外提供超卖存储的每TB价格,这个价格应该比整机的每TB成本低一些。
超卖存储量可以用定时程序收集分组的hdfs存储量,保留最高值,除0.9作为该月实际存储用量,减去分组机器数提供的存储就是超卖量。
超卖比例,前期可以先允许超卖分组存储的20%,并监控集群整体磁盘利用率情况。超卖试运行一段时间后,如果集群存储依然空闲,可以尝试提高超卖比例上限。
目前成本分摊的方式,只支持业务分组按整机打包折算,没有超卖的分摊方式。待评估实际需求后,再来决定是否允许超卖。
如果有业务方需要过高的超卖比例,不适用于现在的集群架构,需要考虑ssd和sata混部的架构。

ssd和sata混部
目前业务按数据量和期望延时的不同分了两个集群,1个是ssd集群,一个是sata盘集群。ssd读写更快,但成本高,存储量有限。
业内最顶尖的做法,是允许ssd和sata盘混部,好处有2。一个是3副本一个是ssd2个是sata盘,保证性能的同时降低存储成本。另一个是时间相关数据按时间分块存储在不同介质上,例如支付宝账单,3个月内ssd,1年内sata盘,一年前的历史数据就存储在更便宜的存储介质上了。历史类数据都适合用这种方式。
混部的方式,在软件,集群架构,机器采购方面都有颇多挑战,是个长期的工作。 查看全部
本文是前几篇文章的小结,介绍如何通过技术手段,挖掘hbase服务的潜力,将分组的服务器数从17台降低到10台。
首发于专栏 :https://zhuanlan.zhihu.com/p/60357239 

目录
确定优化目标
系统资源瓶颈和优化效果
cpu利用率
内存使用
网络IO
磁盘IO
存储量
稳定性
综述
优化细节
合并region
hbase参数调整
未来的工作
近期工作
中远期工作

确定优化目标
沟通交流后,业务方更看重降低成本。数据量梳理后略有降低,保证吞吐,无长期请求堆积前提下可以放宽延时要求。为了更快的进行优化,放宽稳定性可以要求接受短期波动。
另外,该分组的regionserver之前存在不稳定的问题,这次优化也一并解决。

系统资源瓶颈和优化效果
降低成本,即省机器,就是用更少的服务器满足业务需求。需要搞清楚单台服务器的瓶颈在哪。下面分析单台服务器的指标和优化效果。
cpu利用率
因为hbase是重IO,轻计算的服务,一般来说hbase服务cpu利用率较低。优化前单台cpu利用率平均在5%,峰值在10%左右。直接机器减半也没有压力。
空闲的cpu利用率,可以用来置换更高的存储压缩比,节约存储空间。更多的业务线程和gc线程,提高吞吐能力,减少gc对系统吞吐和延时毛刺的影响。
峰值cpu在40%以下,说明cpu资源充足富裕,可以减机器,用cpu换存储空间,提高业务吞吐。


内存使用
256GB内存,datanode 4g,regionserver堆内50g,堆外100g,空闲大概100g。可以提高单机regionserver内存到堆内100g,堆外120g,留10%的空闲。
提高内存的好处很多。单机负载不变的前提下,增加堆内内存,可以降低gc频率减少毛刺出现频率,降低gc时间占比提高吞吐(另外如果mixed gc周期能超过大多数region flush的周期,回收会更有效率)。增加memstore写缓存,可以降低写放大效应,从而降低网络和磁盘IO。降低写放大,还可以减少compact,提高缓存有效性从而增加缓存命中率,进一步降低读IO,减少读请求延时。增加堆外内存,可以缓存命中率。
举个例子,一台服务器提高内存使用量,同时region数变为其他服务器的2倍。堆外内存增加20%而region数翻倍,单纯数字上来看缓存命令率应该下降,实际稳定后命中率保持在96%高于其他服务器的94%,这就是降低compact提高命中率的收益。
增大内存的坏处是,单次gc时间会更长,极端情况下请求延时的毛刺会更明显。另外一些hbase内部的数据结构,如memstore的ConcurrentSkipListMap,对象数太多性能会下降的很厉害,需要结合用户需求,在region数,hbase-site参数,gc参数上综合考虑,做一个权衡。


网络IO
网络In 20MB,Out 40到100MB波动。万兆网卡峰值也就百MB以内,机器数砍半没压力。

磁盘IO
非major compact时段,磁盘读流量较高,高峰期200MB到300MB,主要来自compact。写流量50MB/s。磁盘流量较高是个瓶颈,需要技术优化。
磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5左右),如果不考虑服务端filter过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
用户的业务场景没用filter,所以可以推论读IO主要来自compact ,验证了写放大效应明显。
经过参数优化降低写放大效应,region数不变,单机磁盘读IO下降到之前的1/3,80MB左右。
又经过region合并,参数继续优化,单机两倍region负载,读Io保持在150MB到200MB之间,比之前单倍region负载还要低很多。

存储量
集群存储峰值大概有三个时间点,每周major compact前,每周major compact后,major compact中(大概是major compact前存储*(1+并发major region数/总region数))。
目前没有明确用哪个存储量衡量峰值,一般定期检查,发现用到差不多了就和用户沟通加机器。不允许独立分组存储总量大于分组机器所提供的存储总量。
在major compact前取了数据的总量,刚好跟10台的存储量差不多,可以降低到10台。

稳定性
如系列文章上一篇gc执行细节和参数调优方法论详所说,分组之前存在稳定性的问题。出现问题时需要人工操作迁移region,重启。当机器数减到17台,直接减机器立刻加剧了问题出现的频率。经分析,问题主要出在gc参数上,详见上一篇文章。

综述
通过瓶颈分析,解决了磁盘IO问题后,10台服务器是存储量瓶颈,所以第一步的目标是降到10台。



优化细节
合并region

之前分组17台,单机region数大概500上下,region数太多导致flush都是小文件,写放大罪魁祸首。合并小于10g 的region,将region数降低到单机130+,写放大效应立刻降低了,可以减少机器数了。
一个批量写请求,regionserver内写多个region是串行的,这个角度减少region数可以改善写延时。同时单个region memstore太大,ConcurrentSkipListMap的结构插入性能会降低。目前没发现减少region后明显的写延时差别。杭州的同事分享过阿里对memstore数据结构的优化,版本升级到1.4.8后可以评估能否用的到。
另外,大region会导致major compact压力更大。这个可以通过修改compact policy来解决。

hbase参数调整
增大内存

堆内存 50g->100g
堆外内存 100g->120g
大内存降低写放大效应,提高读缓存命中率,支持更高的吞吐能力。改善了gc表现。


基础配置
hbase.regionserver.handler.count 192->384
hbase.ipc.server.callqueue.read.share 无->0.4

hbase.ipc.server.callqueue.handler.factor 无->0.2
两倍region后,监控看当前handler经常达到192上限,按其他公司经验调大一倍。有时能打到上限。
线程多可以减少队列等待时间,可能增加请求处理时间,监控看,调大能显著减少队列等待时间p99,请求时间p99没有明显变化。
读写分离主要是能降低p99,避免慢写入/高并发scan等耗时长的操作堵塞句柄。192个句柄时,等待队列的p99有时会持续在500ms-1s左右。调大到384,增加读写分离,等待队列p99就维持在100ms以下了。
5个句柄使用一个队列,是为了降低高并发时的锁资源争抢。需要权衡队列比例,如果1,2个句柄就使用一个队列,很容易几个慢请求就把队列里的后续请求都堵住了。目前设置为5:1.

memstrore
hbase.hstore.flusher.count 15-->8 flush线程数
base.regionserver.optionalcacheflushinterval 无->7200000 (1小时到2小时)
hbase.regionserver.hlog.blocksize 无 -> 268435456 (实际值没变,由使用hdfs块大小变为显式设置)
hbase.regionserver.maxlogs 52 -> 200

除了regionserver重启时memstore flush外,有几种情况会触发memstore flush,分别是单个region的memstore达到上限,单个region的memstore距离上次flush过了刷新周期,hlogs达到上限flush涉及的还未flush的region,总memstore达到内存设置上限。这几种情况按照从好到差顺序排列,越靠后对系统的稳定性影响越高,应尽量避免。

堆内存的增加,直接增大了memstore内存上限,增大了单region flush的容量,可以刷大文件减少写放大效应。同时尽量让region写满128MB再flush,可以错开flush时间,错开compact时间,降低磁盘IO峰值,减少flush和compact排队现象。
10台均分region,调整后单台的memstore在10g到30g波动。为了尽量128MB在刷,其他几种flush方式的参数要跟着调整。

虽然我们要避免后面几种flush情况出现,但当业务突然有写入热点,或机器重启各region的memstore重置,可能会触发定时刷新/达到hlogs上限引起集中flush。为降低同时flush并发高引起的问题,降低了并发flush数。由于memstore足够大,单次flush周期长,即使控制flush并发,也不会充暴memstore内存造成写堵塞。
此外,控制flush数可以间接控制minor compact的压力。

按10台regionserver计算规模和请求量,白天绝大多数region在2个内memstore达到128MB,2小时刷新可以确保绝大多数region写满128MB自动刷新,减少写放大。如果按默认1小时,重启后的前2,3天里,会有很多region在相同时间flush,触发compact,系统压力很大。2,3天后,靠着刷新时间每次的随机波动才能慢慢分散开。所以应确保白天峰值写入量时,大多数region都能在刷新周期里写满flush。

hlogs过少会造成刷新周期太短。以之前经常延时变长的一台服务器为例,平均5到10分钟强刷一次,刷的都是小文件,带来了严重的读写放大后果。之前region数三倍于现在,和gc参数不合适一起造成了偶发的gc时间占比高影响业务的问题。 另外,目前hlogs 50的配置会造成同时flush大量region,同时compact,系统压力大,造成请求和吞吐的毛刺。
maxlogs的配置多大合适?maxlogs 从90,到120,150,200,250,300,350,400都实验过,越大的hlogs对缓解region写入不均,调大flush尺寸和周期越有利 。
单纯按照流传的公式来看, 下限 50 * 1024/(256*0.95)= 210,上限60 * 1024/(256*0.95)= 252,应该在210到252之间。实际由于各region会陆续触发flush,hlogs即使到达252,memstore总内存可能只有10-20g,依然可以调大。
hlogs上限提高能解决重启后同时flush密集的问题。重启后,各region memstore都是从0开始缓存,hlogs到上限时如果大量region没写满,会触发大量region同时 flush,这些region的memstore再次清零了,下个周期依然有大量region同时flush,打散速度慢于刷新周期触发的flush。hlogs上限出发的同时flush,compact对gc压力很大,请求延时会周期性显著提升。如果256MB flush,8台服务器,hlogs需要到400。128MB则200即可。
hlogs过多可能有什么影响?1.重启时间变长。重试时间长本质是总memstore增大,flush时间长造成的。之前17台规模单台重启大概1分钟,8台规模单台大概1分50秒,总时间并没有增加。再就是如果某台regionserver挂掉,集群要重读未flush的hlogs,hlogs多了会增加重读的量,增加集群负担。

综上所述,最终flush size定为128MB,hlogs上限定为200。

读cache
hfile.block.cache.size 0.19 ->0.2
hbase.bucketcache.size 102400 -> 132000
hbase.bucketcache.percentage.in.combinedcache 0.9 -> 0.85

缓存大小配合堆内堆外内存增加的调整,缓存变成堆外上限112g,堆内20g。L1和L2的比例按实际线上情况由0.9调整为0.85.


compact
hbase.regionserver.thread.compaction.small 6 -> 8
hbase.regionserver.thread.compaction.large 3 -> 4

略微提高minor compact 和major compact速度,尤其是major compact速度,以便机器减少到一半时,夜里能major完。调到12,gc压力过大,所以只是微调。

hdfs
dfs.client.hedged.read.threadpool.size 50 ->300 
dfs.client.hedged.read.threshold.millis 150->500
hbase.regionserver.hlog.slowsync.ms 无->400
compact负载一高,200线程池会报大量的线程池满,资源不够用,所以调到300。
我们用多路读是为了当磁盘故障时,可以读其他副本。如果超时时间太低,可以读本地的去读了远程副本,显著增大集群网络和磁盘IO。读包括compact的读,是轻延时重吞吐的,集群磁盘IO负载高,延时增加,触发多路读又增大了集群的IO压力。尤其是本地化不是100%时,会读其他机器上的副本,400毫秒也依然容易超时,所以超时时间改为500,确保在一般的高负载和非本地化场景中,也不会给集群额外的压力,只有磁盘真正故障堵塞读的时候再读其他副本上。由于95%以上的读都来自内存缓存,500毫秒的最大超时时间并不会造成显著的读请求延时升高,毕竟常态的gc也要几百ms时间。
负载稍微高点,日志文件满屏都是wal log slow,淹没了其他需要关注的问题。由于wal是单线程顺序写单文件,写入速度是有瓶颈的,调到400,只有负载较高时才会打印。

gc参数调整
请看上一篇,本文不再叙述。

未来的工作
近期工作
打散major compact执行时间

现在集群内所有业务分组同一天晚上进行major compact ,集群网络和磁盘IO压力大,存储量也会同时飙升。major compact即将打散到每周7天的晚上,以降低集群的压力。

换compact policy
compact policy优化,需要用户配合做客户端代码优化,和业务方暂定4月初共建。
现在所有业务用的都是默认的default policy,中规中矩。而该项目业务场景非常适合用Date Tiered Compaction,该策略能极大降低compact写放大效应尤其是major compact的压力,并且提升近期热点数据查询速度。
更换该策略,需要业务方略微修改客户端代码,读写时增加TTL的设置,否则可能会降低查询速度。

换压缩算法
换高压缩比算法gz能进一步减少存储总量,已有经验是可以降低30%左右。即存储量视角,可以降到7台服务器规模。换压缩算法会增加cpu利用率,可能对用户读写造成未知的影响,待4月和用户共建。


中远期工作
2副本

好处是存储量立刻降低1/3,坏处是集群同时坏2块盘的概率比坏3块高得多,更容易丢数据。一般是偏离线,稳定性要求不高,存储量偏高的业务,数据在hive有一份,即使有数据缺失可以很快从hive导一份。需要和用户进一步确定业务模式是否能采用。

超卖
目前成本分摊策略,独立业务分组按regionserver服务器数分摊成本,如果独立业务分组存储总量大于分组机器所提供的存储总量,即认为存储量达到瓶颈。就必须要增加机器,不同的业务瓶颈不一样,按存储量严格限制,不利于提升集群资源的整体利用率。
所以从整个集群的资源利用率来看,允许部分吞吐,延时要求不高的业务使用更多的存储,即存储超卖,可以更有效提高集群资源利用率,释放数据引擎潜力,降低业务方使用成本,为公司省钱。
超卖涉及三个维度。超卖存储每TB价格,超卖存储量如何计算。超卖比例,
现在每TB成本是整机打包折算的,而超卖的存储只涉及纯磁盘的折算成本。要超卖存储,需要额外提供超卖存储的每TB价格,这个价格应该比整机的每TB成本低一些。
超卖存储量可以用定时程序收集分组的hdfs存储量,保留最高值,除0.9作为该月实际存储用量,减去分组机器数提供的存储就是超卖量。
超卖比例,前期可以先允许超卖分组存储的20%,并监控集群整体磁盘利用率情况。超卖试运行一段时间后,如果集群存储依然空闲,可以尝试提高超卖比例上限。
目前成本分摊的方式,只支持业务分组按整机打包折算,没有超卖的分摊方式。待评估实际需求后,再来决定是否允许超卖。
如果有业务方需要过高的超卖比例,不适用于现在的集群架构,需要考虑ssd和sata混部的架构。

ssd和sata混部
目前业务按数据量和期望延时的不同分了两个集群,1个是ssd集群,一个是sata盘集群。ssd读写更快,但成本高,存储量有限。
业内最顶尖的做法,是允许ssd和sata盘混部,好处有2。一个是3副本一个是ssd2个是sata盘,保证性能的同时降低存储成本。另一个是时间相关数据按时间分块存储在不同介质上,例如支付宝账单,3个月内ssd,1年内sata盘,一年前的历史数据就存储在更便宜的存储介质上了。历史类数据都适合用这种方式。
混部的方式,在软件,集群架构,机器采购方面都有颇多挑战,是个长期的工作。

(1.3版)hbase优化之旅(三)-regionserver g1 gc执行细节和参数调优方法论详解

hbasechuxiao 发表了文章 • 0 个评论 • 453 次浏览 • 2019-03-14 17:43 • 来自相关话题

release note
1.3版:调整章节顺序,略微修改概述和标题
1.2版:增加目录信息
1.1版 :增加问题12,补充gc问题挂进程和优化方法论总结的章节,解释最终优化内容。
 
 
目录
本文亮点
gc调优效果
学习的起点-知道自己不知道what
g1 gc执行细节
参数调优方式论
增加日志打印
分析统计信息
分析gc日志
gc问题挂进程
1.写入过猛引起的进程挂
2.参数问题导致mixed gc连续出现长时间暂停
优化方法论小结
最终优化内容

 
本文亮点

讲g1原理和参数语义的文章很多,随便谷歌百度一下无数。但大多数都是对官方介绍的翻译转述。很少有文章介绍参数间彼此的影响,调整参数牵一发动全身的影响有哪些。参数调优的方法论,更是没人提及。

本文目标受众是对g1原理和参数有所了解,想进一步了解参数彼此间联系,想在参数调优方面更进一步的同学。

通过一个线上gc优化的实际案例,带你熟悉gc执行细节,了解我总结的参数调优方法论。

如果对g1不了解,可以先搜索了解一下g1原理和参数后再来看本文,会事半功倍。


gc调优效果

线上某分组的regionserver之前存在不稳定的问题。一两个月内总会随机出现1,2台机器,突然cpu飚高,写延时变高引起堆积,hbase日志和监控找不到原因,只能将问题regionserver移走换一台服务器代替。后来发现regionserver直接重启也能解决,怀疑regionserver配置有问题。

经调研和论证,确定是gc参数配置问题,参数优化后彻底解决了该问题。

由于服务器上还部署了datanode,存在一定的资源抢占,regionserver gc时间,间隔有一些波动,但大体是可衡量的。

之前50g堆,young区最大15g,6秒一次young gc,16线程暂停时间100ms左右。 现在100g堆,控制 20g young区,间隔8秒一次young gc,32线程暂停时间大概90ms。

优化前,大概7分钟一次mixed gc周期,优化后大概半小时。

优化前,mixed gc周期内头几次mixed gc之后,mixed gc快速恶化到400-1500毫秒之间,收尾的几次gc基本要秒级以上,gc时间占比短期内达到80%。优化后,mixed gc绝大多数在400ms以内,百分之几的概率在500-900毫秒范围,gc时间基本不会触发超过10%的告警日志打印。


学习的起点-知道自己不知道what 

看过文档,知道参数语义了,就可以进行参数调优了吗?

学习有这么几个阶段,什么都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在“不知道自己不知道”的阶段,误以为自己什么都知道,贸然调优,事倍功半,优化结果南辕北辙。

请看下面的问题,是否有明确的答案?


1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young区起始比例和最大比例的默认值。那么young区有最小比例吗,最小比例是多少?

2.young区的动态大小除了受-XX:MaxGCPauseMillis=100 单次gc最大暂停时间影响,受-XX:G1MaxNewSizePercent=60上限限制,还被其他因素影响吗?

3.-XX:InitiatingHeapOccupancyPercent=45是启动并发mixed gc的已用内存比例阈值,该比例的分母是当前堆内存。那么分子是old区已用,是young+old区已用,还是堆内存已用?

4.一次mixed gc周期内,mixed gc之间的间隔是怎样的,立刻执行下次,有固定时间间隔,还是受其他条件影响?

5.-XX:G1OldCSetRegionThresholdPercent=10 是单次mixed gc扫old区region数的比例。该比例的分母是old区已用region,young+old区已用region,还是堆总region?该比例设置10就会扫10%的region吗,是否受其他条件影响?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目标次数,该数值实际逻辑是什么,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)要回收region的存活对象内存占比,存活越低回收效果越好。如果存活比例超过85%了,这次mixed gc,这次mixed迭代周期内就一定不会回收了吗?

8.很多技术文章都翻译了官方文档的这句话,mixed gc从可回收比例高的region开始回收。实际执行上是怎么做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次数, -XX:G1OldCSetRegionThresholdPercent=10 是最大扫old区region数的比例,-XX:MaxGCPauseMillis=100 是期待的单次gc最大暂停时间。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)是回收region的存活对象内存占比。这几个参数如何决定单次mixed gc清理多少region,一次mixed周期执行多少次mixed gc?

10.其他条件一样,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的单次gc时间短吗?

11.阅读理解,下面三行mixed gc执行日志,说明存在哪些问题?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]
6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]
6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

 
12.坊间传闻,g1内存越大回收效率越差,越不稳定。即使机器资源够分配100g的堆,也只分配50g。这么做有道理吗?

上面各个参数都标注了网上常见的参数解释,了解参数解释你能回答以上的问题吗?如果有疑惑,那么请看下一节。

 


g1 gc执行细节

-XX:G1NewSizePercent同时是young区最小比例。

young区上限还受old区大小影响,最大不超过90%(默认保留10%)-当前old区大小。当old区朝90%接近时,young区会持续减少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old区已用。

mixed gc之间是以触发下限young区gc为间隔的,即下限eden区写满时,mixed gc同时清理young区和old区。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆总region数,100g堆32MB大小region,则总数3200个region,10%就是320个。该比例决定单次mixed gc扫描region数的上限。如果预期扫region时间高于预期剩余时间,则只会扫少量region计算活跃对象比例。

-XX:G1MixedGCCountTarget=8语义是,用触发mixed gc周期时old区的已用region数,除以8,作为每次mixed gc逻辑上最少回收的region数。

一次mixed gc扫描的region中,活跃对象比例高于-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代价高。

如果不昂贵的region数不小于最小回收数,那么回收所有不昂贵region,昂贵region按活跃比例在mixed gc周期内整体排序。如果扫描完所有old区region,垃圾比例依然高于-XX:G1HeapWastePercent比例,那么按活跃对象比逆序清理昂贵region,直到比例降低到阈值。

如果总region数大于最小region数但不昂贵region数不够min,则回收min个region,昂贵region中活跃对象比最低的region填补min的缺。

如果min大于实际扫的region,会回收本次mixed gc所有扫瞄过的region数,即使region的活跃对象比超过阈值。

如果-XX:MaxGCPauseMillis过低,预期扫region时间远大于预期剩余时间,那么实际扫的region会小于min数,即使扫的都是昂贵region,依然会全部回收,造成数秒级gc暂停,实际暂停时间反而比-XX:MaxGCPauseMillis大一些要长。

若干次mixed gc后,如果可回收占堆内存比例小于等于-XX:G1HeapWastePercent,本轮mixed gc周期结束。

 

综上所述,参数有密切的关联关系,调优需要全局权衡。

最后一个问题的日志,由于-XX:MaxGCPauseMillis过低只扫描了少量region,-XX:G1MixedGCCountTarget过低min region数高,昂贵region依然要被回收,暂停时间远大于预期到达秒级,下次扫的region更少,回收昂贵region难以释放内存,持续恶化。堆50g,young区下限2.5g ,间隔不到1秒一次mixed gc,gc时间占比很快超过80%。再加上偶发的memstore内存接近峰值,加上L1读cache,加上静态对象占用的,总不可释放内存比例很高,而-XX:InitiatingHeapOccupancyPercent比例过低,触发mixed gc周期时几乎拷贝了一遍old区所有region,内存也没释放多少空间,regionserver表现出持续的吞吐能力降低,服务不可用现象。
 
目前只遗留了一个问题,g1是否可以用大堆。容我卖个关子,读者可以结合上面的执行细节,先自己认真思考一下,再往下看。

 
参数调优方式论

授人以鱼不如授人以渔,我们先来探讨调优的方法论。

调优方法论是,先整体分析gc运行状况,找到瓶颈点或怀疑有问题的地方。仔细翻阅问题发生时间的gc日志,找到有问题的信息,调优。继续观察。


增加日志打印

调优首先要清楚gc运行状况,上一篇gc探索分享里介绍了如何加打印参数,以及如何通过gceasy可视化统计信息。如果没阅读请先看上一篇相关内容。需要额外注意的是,gceasy对g1支持的有点小bug,gc暂停时间图把mixed gc统计到young gc次数里了。如有图里有暂停时间比-XX:MaxGCPauseMillis高一个数量级的暂停时间,都是mixed gc的。

 
分析统计信息

通过gceasy,我们可以看到gc平均暂停时间,最大暂停时间,应用吞吐占比。gc暂停时间分布,gc前后堆内存变化,gc暂停时间按时间周期的分布图,单次gc垃圾回收内存量的分布图,young区容量分布图,old区容量分布图,young区 promoted分布图,gc内部各阶段时间统计对比,对象创建和升代速度。

通过这些统计信息我们可以了解系统的运行情况,如young 区在多大范围波动,平均young区 gc间隔大概是多久,升代量是多少,mixed gc周期的间隔,每次mixed gc周期回收的old 区内存,等等。掌握这些统计信息,我们就对系统运行情况有了基本了解,可以对系统是否健康做一个初步判断。

不健康主要有两大类,单次暂停时间过长,gc时间占比高。这两个问题又可以引申出单次gc暂停时间过长的次数太多,gc时间占比过高的频率高,mixed gc频率高,每次mixed回收old区内存量太低,等等。

我的经验是,50g或100g的堆内存,如果gc时间占比超过5%,或者gc最大暂停时间超过5秒出现很多次,都是有问题的,需要优化。

如果gc日志跨越好几天,周期性gc时间占比高,但平均gc时间占比可能并不高。如果某一时间段吞吐,延时有问题,可以将这个时间段前后半小时的gc日志截出来,单独上传gceasy。

只看统计信息还不够。本人遇到过日志周期跨越好几天,平均gc时间占比不到2%,最大暂停时间1秒110毫秒,但图表上来看1秒上下的暂停总是连续出现,伴随着周期性业务响应变慢。仔细查看gc日志,gc时间占比在短时间内达到了70%以上,周期性出现。所以需要进一步分析gc日志。


分析gc日志

gc日志内容很多,包括每次gc前堆情况,gc后堆情况,young和mixed gc执行情况。那么我们如何分析呢?

还是要对症下药,找问题出现时前后的日志。

如果暂停时间周期性或偶发出现比预期高一个数量级,可以根据统计信息看到的长时间暂停时间。搜日志,如搜索 real=5找暂停时间5秒到6秒的 ,找到gc上下文,分析为什么慢。


常见以下几种问题

1. max小于等于min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]
2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]
2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

没啥说的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的经验是min最好控制在max的1/4以内,留冗余给昂贵region。

 

2.predicted time is too high

 60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 regions]
 
 60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time:0.00 ms]

 

由于期望gc执行时间短,预期时间太长,只会扫很少的old区region,甚至可能比min region还少,当遇到连续的高代价region,即使是100%活跃的region也要拷贝,执行时间更长,下次预期时间更长,扫的region数更少,进入恶性循环。单次mixed gc释放不了多少内存,gc时间占比越来越高,有zk超时风险。

本质上,这是设置期望时间太短反而造成暂停时间更长,需要放宽期望gc执行时间,减少young 区最小值,以增大回收old区的可用时间。降低-XX:G1OldCSetRegionThresholdPercent比例以降低预期时间。内存使用上,让不可回收内存比例低一些,避免高存活比例region连续出现的概率,即增大堆内存,增大old区回收阈值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低写放大,降低磁盘读写IO,增大L1缓存可以提高读缓存命中率。所以这不单单是gc自己的问题,要系统性综合考虑,确定系统的瓶颈究竟是什么,优化哪个问题更重要。

 

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytes (15.00 %), threshold: 15.00 %]
 
 61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time:0.00 ms]

 
到达垃圾保留比例,最后一次mixed gc只会扫很少的region,如果正好都是昂贵的region,则拷贝代价很高。
运气不好的话,这几乎是不可避免的。调大-XX:G1OldCSetRegionThresholdPercent理论上可以让最后一次扫region的平均数量变大,但会造成predicted time is too high更频繁出现。增加堆内存上限和old区回收阈值,提高-XX:G1HeapWastePercent比例,可以更早结束垃圾mixed gc周期,最后一次扫描都是昂贵region的概率也降低了。调大-XX:G1MixedGCCountTarget 让min region更少,可能每次回收量减少一次回收周期时间拉长,需要配合更高的垃圾浪费率和更低的-XX:G1MixedGCLiveThresholdPercent比例,达到快速清理的效果。


gc问题挂进程

gc问题的极端后果是进程挂掉。一般经验认为,内存增加比释放快,内存不足,full gc, oom,进程就挂了。

我遇到过多次gc引起进程挂掉,但目前还没遇到过g1的oom,甚至都没遇到过g1的full gc。这是因为regionserver内存模型young区升代比例很低,另外g1在恶劣条件下gc时间占比很高,即使regionserver压力很大,还没到full gc,就gc时间占比过高引起zk session超时退出了。下面举两个例子。

1.写入过猛引起的进程挂

业务方补一年的数据,hadoop作业写入过猛,还有热点,flush一直排队,compact一直排队,甚至触发hfile上限堵塞写了。写的p99一度飙升到30秒,young gc一次升old区很多,old区内存增长比正常快。结果还没到old区触发mixed gc,由于young gc达到了1秒2,3次,gc时间占比一度超过了95%,开始出现zk session超时,regionserver自动退出。

可以调大region hfile数上限来缓解,但治标不治本。需要控制用户写入,加quota来限制。

 

2.参数问题导致mixed gc连续出现长时间暂停

regionserver有一定压力,在承受范围内而进程挂了。这是由于参数设置有问题,由于期待暂停时间过低扫的region数不够多,又都是不可回收region,暂停时间越来越长,几次达到8,9秒暂停后,zk session超时退出。

这个按上面的redicted time is too high问题来优化即可。

 

优化方法论小结

1.-XX:ParallelGCThreads官方推荐是逻辑cpu核数的5/8,注意逻辑cpu核数是物理核的2倍,所以24核可以开到32,young和mixed gc确实变快了。

2.参数调整确保每轮mixed gc的max region数是min region数的4倍以上,降低都是昂贵region的几率。

3.适量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暂停时间。目的是留给扫描region充足时间,确保每轮mixed gc扫描的region数和期待的max region数相似。

4.如果不想young gc时间也同步变长,可以通过-XX:G1MaxNewSizePercent降低young区最大比例来控制young gc时间。

5.降低最小young 区比例,可以降低mixed gc时回收young 区的时间,从而增加扫描old区region时间,确保扫描更多region。

6.触发mixed gc周期时,old区可回收内存比例越高,越不容易遇到连续昂贵ergion,回收越有效率。所以应该调大堆内存,调高mixed gc触发阈值,控制不可回收内存比例(即memstore和L1 block cache)。

7.当前面条件都满足时,每次mixed gc周期可回收内存比例很高,每轮mixed gc扫描的region数几倍于min region有充足的region挑选不昂贵ergion,可以调高-XX:G1HeapWastePercent比例让本轮mixed gc尽快结束,降低-XX:G1MixedGCLiveThresholdPercent优先回收活跃对象更少的region。


最终优化内容

-Xmx100g -Xms100g   50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent=   65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

 

 

 
  查看全部
release note
1.3版:调整章节顺序,略微修改概述和标题
1.2版:增加目录信息
1.1版 :增加问题12,补充gc问题挂进程和优化方法论总结的章节,解释最终优化内容。
 
 
目录
本文亮点
gc调优效果
学习的起点-知道自己不知道what
g1 gc执行细节
参数调优方式论
增加日志打印
分析统计信息
分析gc日志
gc问题挂进程
1.写入过猛引起的进程挂
2.参数问题导致mixed gc连续出现长时间暂停
优化方法论小结
最终优化内容

 
本文亮点

讲g1原理和参数语义的文章很多,随便谷歌百度一下无数。但大多数都是对官方介绍的翻译转述。很少有文章介绍参数间彼此的影响,调整参数牵一发动全身的影响有哪些。参数调优的方法论,更是没人提及。

本文目标受众是对g1原理和参数有所了解,想进一步了解参数彼此间联系,想在参数调优方面更进一步的同学。

通过一个线上gc优化的实际案例,带你熟悉gc执行细节,了解我总结的参数调优方法论。

如果对g1不了解,可以先搜索了解一下g1原理和参数后再来看本文,会事半功倍。


gc调优效果

线上某分组的regionserver之前存在不稳定的问题。一两个月内总会随机出现1,2台机器,突然cpu飚高,写延时变高引起堆积,hbase日志和监控找不到原因,只能将问题regionserver移走换一台服务器代替。后来发现regionserver直接重启也能解决,怀疑regionserver配置有问题。

经调研和论证,确定是gc参数配置问题,参数优化后彻底解决了该问题。

由于服务器上还部署了datanode,存在一定的资源抢占,regionserver gc时间,间隔有一些波动,但大体是可衡量的。

之前50g堆,young区最大15g,6秒一次young gc,16线程暂停时间100ms左右。 现在100g堆,控制 20g young区,间隔8秒一次young gc,32线程暂停时间大概90ms。

优化前,大概7分钟一次mixed gc周期,优化后大概半小时。

优化前,mixed gc周期内头几次mixed gc之后,mixed gc快速恶化到400-1500毫秒之间,收尾的几次gc基本要秒级以上,gc时间占比短期内达到80%。优化后,mixed gc绝大多数在400ms以内,百分之几的概率在500-900毫秒范围,gc时间基本不会触发超过10%的告警日志打印。


学习的起点-知道自己不知道what 

看过文档,知道参数语义了,就可以进行参数调优了吗?

学习有这么几个阶段,什么都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在“不知道自己不知道”的阶段,误以为自己什么都知道,贸然调优,事倍功半,优化结果南辕北辙。

请看下面的问题,是否有明确的答案?


1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young区起始比例和最大比例的默认值。那么young区有最小比例吗,最小比例是多少?

2.young区的动态大小除了受-XX:MaxGCPauseMillis=100 单次gc最大暂停时间影响,受-XX:G1MaxNewSizePercent=60上限限制,还被其他因素影响吗?

3.-XX:InitiatingHeapOccupancyPercent=45是启动并发mixed gc的已用内存比例阈值,该比例的分母是当前堆内存。那么分子是old区已用,是young+old区已用,还是堆内存已用?

4.一次mixed gc周期内,mixed gc之间的间隔是怎样的,立刻执行下次,有固定时间间隔,还是受其他条件影响?

5.-XX:G1OldCSetRegionThresholdPercent=10 是单次mixed gc扫old区region数的比例。该比例的分母是old区已用region,young+old区已用region,还是堆总region?该比例设置10就会扫10%的region吗,是否受其他条件影响?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目标次数,该数值实际逻辑是什么,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)要回收region的存活对象内存占比,存活越低回收效果越好。如果存活比例超过85%了,这次mixed gc,这次mixed迭代周期内就一定不会回收了吗?

8.很多技术文章都翻译了官方文档的这句话,mixed gc从可回收比例高的region开始回收。实际执行上是怎么做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次数, -XX:G1OldCSetRegionThresholdPercent=10 是最大扫old区region数的比例,-XX:MaxGCPauseMillis=100 是期待的单次gc最大暂停时间。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)是回收region的存活对象内存占比。这几个参数如何决定单次mixed gc清理多少region,一次mixed周期执行多少次mixed gc?

10.其他条件一样,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的单次gc时间短吗?

11.阅读理解,下面三行mixed gc执行日志,说明存在哪些问题?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]
6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]
6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

 
12.坊间传闻,g1内存越大回收效率越差,越不稳定。即使机器资源够分配100g的堆,也只分配50g。这么做有道理吗?

上面各个参数都标注了网上常见的参数解释,了解参数解释你能回答以上的问题吗?如果有疑惑,那么请看下一节。

 


g1 gc执行细节

-XX:G1NewSizePercent同时是young区最小比例。

young区上限还受old区大小影响,最大不超过90%(默认保留10%)-当前old区大小。当old区朝90%接近时,young区会持续减少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old区已用。

mixed gc之间是以触发下限young区gc为间隔的,即下限eden区写满时,mixed gc同时清理young区和old区。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆总region数,100g堆32MB大小region,则总数3200个region,10%就是320个。该比例决定单次mixed gc扫描region数的上限。如果预期扫region时间高于预期剩余时间,则只会扫少量region计算活跃对象比例。

-XX:G1MixedGCCountTarget=8语义是,用触发mixed gc周期时old区的已用region数,除以8,作为每次mixed gc逻辑上最少回收的region数。

一次mixed gc扫描的region中,活跃对象比例高于-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代价高。

如果不昂贵的region数不小于最小回收数,那么回收所有不昂贵region,昂贵region按活跃比例在mixed gc周期内整体排序。如果扫描完所有old区region,垃圾比例依然高于-XX:G1HeapWastePercent比例,那么按活跃对象比逆序清理昂贵region,直到比例降低到阈值。

如果总region数大于最小region数但不昂贵region数不够min,则回收min个region,昂贵region中活跃对象比最低的region填补min的缺。

如果min大于实际扫的region,会回收本次mixed gc所有扫瞄过的region数,即使region的活跃对象比超过阈值。

如果-XX:MaxGCPauseMillis过低,预期扫region时间远大于预期剩余时间,那么实际扫的region会小于min数,即使扫的都是昂贵region,依然会全部回收,造成数秒级gc暂停,实际暂停时间反而比-XX:MaxGCPauseMillis大一些要长。

若干次mixed gc后,如果可回收占堆内存比例小于等于-XX:G1HeapWastePercent,本轮mixed gc周期结束。

 

综上所述,参数有密切的关联关系,调优需要全局权衡。

最后一个问题的日志,由于-XX:MaxGCPauseMillis过低只扫描了少量region,-XX:G1MixedGCCountTarget过低min region数高,昂贵region依然要被回收,暂停时间远大于预期到达秒级,下次扫的region更少,回收昂贵region难以释放内存,持续恶化。堆50g,young区下限2.5g ,间隔不到1秒一次mixed gc,gc时间占比很快超过80%。再加上偶发的memstore内存接近峰值,加上L1读cache,加上静态对象占用的,总不可释放内存比例很高,而-XX:InitiatingHeapOccupancyPercent比例过低,触发mixed gc周期时几乎拷贝了一遍old区所有region,内存也没释放多少空间,regionserver表现出持续的吞吐能力降低,服务不可用现象。
 
目前只遗留了一个问题,g1是否可以用大堆。容我卖个关子,读者可以结合上面的执行细节,先自己认真思考一下,再往下看。

 
参数调优方式论

授人以鱼不如授人以渔,我们先来探讨调优的方法论。

调优方法论是,先整体分析gc运行状况,找到瓶颈点或怀疑有问题的地方。仔细翻阅问题发生时间的gc日志,找到有问题的信息,调优。继续观察。


增加日志打印

调优首先要清楚gc运行状况,上一篇gc探索分享里介绍了如何加打印参数,以及如何通过gceasy可视化统计信息。如果没阅读请先看上一篇相关内容。需要额外注意的是,gceasy对g1支持的有点小bug,gc暂停时间图把mixed gc统计到young gc次数里了。如有图里有暂停时间比-XX:MaxGCPauseMillis高一个数量级的暂停时间,都是mixed gc的。

 
分析统计信息

通过gceasy,我们可以看到gc平均暂停时间,最大暂停时间,应用吞吐占比。gc暂停时间分布,gc前后堆内存变化,gc暂停时间按时间周期的分布图,单次gc垃圾回收内存量的分布图,young区容量分布图,old区容量分布图,young区 promoted分布图,gc内部各阶段时间统计对比,对象创建和升代速度。

通过这些统计信息我们可以了解系统的运行情况,如young 区在多大范围波动,平均young区 gc间隔大概是多久,升代量是多少,mixed gc周期的间隔,每次mixed gc周期回收的old 区内存,等等。掌握这些统计信息,我们就对系统运行情况有了基本了解,可以对系统是否健康做一个初步判断。

不健康主要有两大类,单次暂停时间过长,gc时间占比高。这两个问题又可以引申出单次gc暂停时间过长的次数太多,gc时间占比过高的频率高,mixed gc频率高,每次mixed回收old区内存量太低,等等。

我的经验是,50g或100g的堆内存,如果gc时间占比超过5%,或者gc最大暂停时间超过5秒出现很多次,都是有问题的,需要优化。

如果gc日志跨越好几天,周期性gc时间占比高,但平均gc时间占比可能并不高。如果某一时间段吞吐,延时有问题,可以将这个时间段前后半小时的gc日志截出来,单独上传gceasy。

只看统计信息还不够。本人遇到过日志周期跨越好几天,平均gc时间占比不到2%,最大暂停时间1秒110毫秒,但图表上来看1秒上下的暂停总是连续出现,伴随着周期性业务响应变慢。仔细查看gc日志,gc时间占比在短时间内达到了70%以上,周期性出现。所以需要进一步分析gc日志。


分析gc日志

gc日志内容很多,包括每次gc前堆情况,gc后堆情况,young和mixed gc执行情况。那么我们如何分析呢?

还是要对症下药,找问题出现时前后的日志。

如果暂停时间周期性或偶发出现比预期高一个数量级,可以根据统计信息看到的长时间暂停时间。搜日志,如搜索 real=5找暂停时间5秒到6秒的 ,找到gc上下文,分析为什么慢。


常见以下几种问题

1. max小于等于min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]
2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]
2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

没啥说的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的经验是min最好控制在max的1/4以内,留冗余给昂贵region。

 

2.predicted time is too high

 60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 regions]
 
 60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time:0.00 ms]

 

由于期望gc执行时间短,预期时间太长,只会扫很少的old区region,甚至可能比min region还少,当遇到连续的高代价region,即使是100%活跃的region也要拷贝,执行时间更长,下次预期时间更长,扫的region数更少,进入恶性循环。单次mixed gc释放不了多少内存,gc时间占比越来越高,有zk超时风险。

本质上,这是设置期望时间太短反而造成暂停时间更长,需要放宽期望gc执行时间,减少young 区最小值,以增大回收old区的可用时间。降低-XX:G1OldCSetRegionThresholdPercent比例以降低预期时间。内存使用上,让不可回收内存比例低一些,避免高存活比例region连续出现的概率,即增大堆内存,增大old区回收阈值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低写放大,降低磁盘读写IO,增大L1缓存可以提高读缓存命中率。所以这不单单是gc自己的问题,要系统性综合考虑,确定系统的瓶颈究竟是什么,优化哪个问题更重要。

 

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytes (15.00 %), threshold: 15.00 %]
 
 61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time:0.00 ms]

 
到达垃圾保留比例,最后一次mixed gc只会扫很少的region,如果正好都是昂贵的region,则拷贝代价很高。
运气不好的话,这几乎是不可避免的。调大-XX:G1OldCSetRegionThresholdPercent理论上可以让最后一次扫region的平均数量变大,但会造成predicted time is too high更频繁出现。增加堆内存上限和old区回收阈值,提高-XX:G1HeapWastePercent比例,可以更早结束垃圾mixed gc周期,最后一次扫描都是昂贵region的概率也降低了。调大-XX:G1MixedGCCountTarget 让min region更少,可能每次回收量减少一次回收周期时间拉长,需要配合更高的垃圾浪费率和更低的-XX:G1MixedGCLiveThresholdPercent比例,达到快速清理的效果。


gc问题挂进程

gc问题的极端后果是进程挂掉。一般经验认为,内存增加比释放快,内存不足,full gc, oom,进程就挂了。

我遇到过多次gc引起进程挂掉,但目前还没遇到过g1的oom,甚至都没遇到过g1的full gc。这是因为regionserver内存模型young区升代比例很低,另外g1在恶劣条件下gc时间占比很高,即使regionserver压力很大,还没到full gc,就gc时间占比过高引起zk session超时退出了。下面举两个例子。

1.写入过猛引起的进程挂

业务方补一年的数据,hadoop作业写入过猛,还有热点,flush一直排队,compact一直排队,甚至触发hfile上限堵塞写了。写的p99一度飙升到30秒,young gc一次升old区很多,old区内存增长比正常快。结果还没到old区触发mixed gc,由于young gc达到了1秒2,3次,gc时间占比一度超过了95%,开始出现zk session超时,regionserver自动退出。

可以调大region hfile数上限来缓解,但治标不治本。需要控制用户写入,加quota来限制。

 

2.参数问题导致mixed gc连续出现长时间暂停

regionserver有一定压力,在承受范围内而进程挂了。这是由于参数设置有问题,由于期待暂停时间过低扫的region数不够多,又都是不可回收region,暂停时间越来越长,几次达到8,9秒暂停后,zk session超时退出。

这个按上面的redicted time is too high问题来优化即可。

 

优化方法论小结

1.-XX:ParallelGCThreads官方推荐是逻辑cpu核数的5/8,注意逻辑cpu核数是物理核的2倍,所以24核可以开到32,young和mixed gc确实变快了。

2.参数调整确保每轮mixed gc的max region数是min region数的4倍以上,降低都是昂贵region的几率。

3.适量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暂停时间。目的是留给扫描region充足时间,确保每轮mixed gc扫描的region数和期待的max region数相似。

4.如果不想young gc时间也同步变长,可以通过-XX:G1MaxNewSizePercent降低young区最大比例来控制young gc时间。

5.降低最小young 区比例,可以降低mixed gc时回收young 区的时间,从而增加扫描old区region时间,确保扫描更多region。

6.触发mixed gc周期时,old区可回收内存比例越高,越不容易遇到连续昂贵ergion,回收越有效率。所以应该调大堆内存,调高mixed gc触发阈值,控制不可回收内存比例(即memstore和L1 block cache)。

7.当前面条件都满足时,每次mixed gc周期可回收内存比例很高,每轮mixed gc扫描的region数几倍于min region有充足的region挑选不昂贵ergion,可以调高-XX:G1HeapWastePercent比例让本轮mixed gc尽快结束,降低-XX:G1MixedGCLiveThresholdPercent优先回收活跃对象更少的region。


最终优化内容

-Xmx100g -Xms100g   50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent=   65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

 

 

 
 

hbase优化之旅(二)regionserver的G1 GC优化探索

hbasechuxiao 发表了文章 • 1 个评论 • 547 次浏览 • 2019-03-06 17:09 • 来自相关话题

优化的最终目的是保障现有用户体验的同时,减少机器,节约成本。

为了更好的编写本文和后续文章,花费20美金。欢迎去附录链接专栏赞赏支持。

g1介绍
g1特点

g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。

g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在预期范围内。

g1适用场景

对平均响应时间,最大响应时间有严格要求的应用系统,如hbase regionserver。

优化原则
先优化业务层和应用层

系统调优是从业务到实现,从整体到局部,从架构到具体组件的。在进行gc调优之前,我们应该确保业务层和应用层已经评估优化过。业务层和应用层的优化一般来说更容易有收益,我们不能指望一个架构设计有缺陷,应用层代码有很多已知问题的系统,通过gc调优一劳永逸。

gc调优3选2原则

先来看一下衡量gc的指标有哪些。对应用吞吐量的影响(一般是gc对cpu的消耗),对延迟的影响,总内存占用(gc触发时留有内存业务可以继续,留有内存做对象拷贝碎片整理等操作,不能oom)。

GC调优3选2原则: 在吞吐量、延迟、内存占用上,我们只能选择其中两个进行调优,无法三者兼得。

hbase已有业务regionserver的调优目标

在调优之前,必须要有明确的性能优化目标, 然后找到未达到该目标的性能瓶颈。再针对瓶颈做优化。通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

hbase集群启用了group分组,重要业务有独立的regionserver分组。

重要业务regionserver的调优目标是,在满足业务延迟要求的基础上,用尽量低的成本,满足业务吞吐量的峰值需求。

也就是说,总吞吐量固定,延迟要求固定,单机cpu和内存固定,求最小机器数。

再转换一下,对单机来说,延迟指标确定,将单机吞吐在单机cpu和内存允许的范围内调整到最大。

需要说明的是,单机能承担多少吞吐,跟业务访问模型,region数,读写缓存参数,网络IO,磁盘IO都有关系。业务和hbase参数的调整应该在gc优化之前进行,网络和磁盘IO一般是应用层优化的。所以下文假设业务层和应用层已优化完毕,网络和磁盘都不是瓶颈,只聚焦在gc参数调优。

本文假设我们换算后的延迟目标是平均gc暂停时间100ms,最大暂停时间2s,gc时间占比3%以内。实际达到这个目标后,还要通过regionserver监控确定请求的延时要是否在用户用户的要求范围内。

影响延迟的因素

gc的时间占比。平均stw gc时间,频率。毛刺stw gc时间,频率。峰值stw gc时间,频率。

一般来说,regionserver应该避免full gc。

新生代越大,单次young gc时间越长,频率越低。

mixed gc受gc触发时机,gc并发线程数,预期迭代次数,每个迭代回收分片比例等多个参数影响,详见附录官方文档。

关于JVM版本

目前生产环境用1.8.0_77, 小米hbase环境用1.8.0_111, Oracle jdk的8最新版本是8u201。

intel性能测试见附录,jdk7不同版本间g1性能差距很大。Jdk7u21升级到jdk7u60,gc以及stw gc的平均时间,最大时间,时间分布都有大幅优化。

所以应该尽量用最新版本的JDK。

优化步骤

需要有方法论判断当前是否应该继续优化。

根据业务对延迟的需求,比较现在的请求延迟和gc情况,预估能接受的平均gc暂停时间,最大gc 暂停时间范围。

关掉自动balance,给一台regionserver少量增加region从而增加单机吞吐。当请求延迟超过用户要求的警戒线后,分析gc日志,找到瓶颈,优化降低gc延迟从而降低请求延迟,以便继续增加region。

当单机region数过多(可以考虑合并region),cpu负载过高,请求延迟无法降下来,任意一个条件满足,单机优化结束。稳定运行一段时间后,尝试将优化推广到整个业务分组。

增加日志


要分析gc情况一定要有gc日志。之前的日志参数如下-XX:+PrintGCDetails gc细节
-XX:+PrintGCDateStamps 时间戳
-Xloggc:${HBASE_LOG_DIR}/gc-`date +'%Y%m%d%H%M'` gc文件格式
-XX:+UseGCLogFileRotation gc文件循环
-XX:NumberOfGCLogFiles=10 文件数
-XX:GCLogFileSize=512M 文件大小
-XX:+HeapDumpOnOutOfMemoryError oom时堆dump
-XX:HeapDumpPath=${HBASE_LOG_DIR}/hbase.heapdump dump目录
-XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
-XX:+PrintAdaptiveSizePolicy 打印自适应收集的大小
-XX:+PrintFlagsFinal 打印参数值
参考其他优化的文章,增加打印参数-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间
-XX:+PrintTenuringDistribution https://www.jianshu.com/p/e634955f3bbb survivor分布情况
-XX:+PrintHeapAtGC gc前后打印堆信息
-XX:+PrintSafepointStatistics https://blog.csdn.net/u0119182 ... 47159 分析安全点统计信息,优化gc参考
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1 打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks。似乎cms更有用
gc日志太多可能会影响性能,目前没有日志对性能影响的数据,暂不考虑日志对性能的影响。


可视化

有很多gc可视化的工具。比如在线的gceasy https://gceasy.io/index.jsp#banner,上传gc日志可以进行分析。免费功能有各种图表展示。20美金一个月可以使用高级功能。

本文用gceasy做例子,其他可视化工具介绍见附录。

下面从前文优化过的节点开始,分析gc日志。








 
问题分析
gceasy优化建议

不花钱怎么变强?开启gceasy高级功能。


单次gc暂停最长到5秒了。链接给出了优化建议,见附录。

首先是优化程序降低对象创建速度。现在平均对象创建速度是1.22gb/sec。减少对象创建速度主要是更多用堆外内存,优化写放大,本文重点是gc优化,不展开。

再是年轻代太小,增大年轻代。这规则适用于其他gc,而g1 young gc是动态的,mixed gc是分次迭代的,young gc不能太大,否则young gc反而会比单次mixed慢。

再就是设置期待的最大暂停时间,目前设置100ms,暂不修改。

服务器上进程多,内存交换,服务器无这个情况。

gc线程不够多,有可能。但gc3秒以上的只有6次,差不多每天一次。如果是线程数明显不够次数应该更多,先放放。

服务器高IO导致的等待时间长。有可能,有datanode,需要进一步分析gc日志和IO监控。

没有禁用system.gc()。gc case表格来看没有相关调用。

堆太大。有可能,毕竟80GB堆。但大堆对吞吐有好处。

看5秒暂停gc的具体日志,{Heap before GC invocations=50103 (full 0):
garbage-first heap total 83886080K, used 48760053K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 5 survivors (163840K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
2019-02-18T17:48:53.570+0800: 365568.567: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 268435456 bytes, new threshold 1 (max 1)
- age 1: 87997400 bytes, 87997400 total
365568.567: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 3313913, predicted base time: 1151.36 ms, remaining time: 0.00 ms, target pause time: 100.00 ms]
365568.567: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 123 regions, survivors: 5 regions, predicted young region time: 50.17 ms]
365568.583: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 70 regions, max: 256 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
365568.583: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 70 regions, expensive: 70 regions, min: 80 regions, remaining time: 0.00 ms]
365568.583: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 123 regions, survivors: 5 regions, old: 70 regions, predicted pause time: 4353.85 ms, target pause time: 100.00 ms]
365573.866: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 40.96 %, threshold: 10.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
365573.866: [G1Ergonomics (Mixed GCs) do not continue mixed GCs, reason: reclaimable percentage not over threshold, candidate old regions: 407 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
, 5.2999017 secs]
[Parallel Time: 5227.7 ms, GC Workers: 16]
[GC Worker Start (ms): Min: 365568584.3, Avg: 365568584.3, Max: 365568584.4, Diff: 0.1]
[Ext Root Scanning (ms): Min: 1.3, Avg: 1.6, Max: 3.9, Diff: 2.7, Sum: 25.1]
[Update RS (ms): Min: 1065.2, Avg: 1067.2, Max: 1067.8, Diff: 2.6, Sum: 17075.4]
[Processed Buffers: Min: 739, Avg: 831.5, Max: 879, Diff: 140, Sum: 13304]
[Scan RS (ms): Min: 3610.8, Avg: 3611.6, Max: 3612.6, Diff: 1.8, Sum: 57786.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.3, Diff: 0.3, Sum: 0.5]
[Object Copy (ms): Min: 545.2, Avg: 546.3, Max: 547.2, Diff: 2.0, Sum: 8741.5]
[Termination (ms): Min: 0.1, Avg: 0.6, Max: 0.8, Diff: 0.7, Sum: 9.8]
[Termination Attempts: Min: 1, Avg: 465.3, Max: 552, Diff: 551, Sum: 7445]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 5227.3, Avg: 5227.4, Max: 5227.5, Diff: 0.2, Sum: 83638.8]
[GC Worker End (ms): Min: 365573811.8, Avg: 365573811.8, Max: 365573811.8, Diff: 0.0]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 3.4 ms]
[Other: 68.4 ms]
[Choose CSet: 16.3 ms]
[Ref Proc: 3.6 ms]
[Ref Enq: 0.2 ms]
[Redirty Cards: 8.7 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 31.6 ms]
[Eden: 3936.0M(3936.0M)->0.0B(3904.0M) Survivors: 160.0M->192.0M Heap: 46.5G(80.0G)->42.3G(80.0G)]
Heap after GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 44323410K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 6 young (196608K), 6 survivors (196608K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
}
[Times: user=85.30 sys=0.00, real=5.30 secs]
2019-02-18T17:48:58.871+0800: 365573.867: Total time for which application threads were stopped: 5.3131110 seconds, Stopping threads took: 0.0005771 seconds
2019-02-18T17:48:58.896+0800: 365573.893: Total time for which application threads were stopped: 0.0186168 seconds, Stopping threads took: 0.0005541 seconds
2019-02-18T17:48:58.906+0800: 365573.902: Total time for which application threads were stopped: 0.0078098 seconds, Stopping threads took: 0.0005988 seconds
2019-02-18T17:48:58.915+0800: 365573.912: Total time for which application threads were stopped: 0.0075271 seconds, Stopping threads took: 0.0003996 seconds
2019-02-18T17:48:58.923+0800: 365573.919: Total time for which application threads were stopped: 0.0072014 seconds, Stopping threads took: 0.0003114 seconds
2019-02-18T17:48:58.934+0800: 365573.930: Total time for which application threads were stopped: 0.0078706 seconds, Stopping threads took: 0.0005490 seconds
2019-02-18T17:48:58.942+0800: 365573.938: Total time for which application threads were stopped: 0.0073198 seconds, Stopping threads took: 0.0003927 seconds
2019-02-18T17:48:58.964+0800: 365573.960: Total time for which application threads were stopped: 0.0078810 seconds, Stopping threads took: 0.0007641 seconds
2019-02-18T17:48:58.972+0800: 365573.969: Total time for which application threads were stopped: 0.0075881 seconds, Stopping threads took: 0.0006277 seconds
2019-02-18T17:48:58.981+0800: 365573.978: Total time for which application threads were stopped: 0.0081290 seconds, Stopping threads took: 0.0011246 seconds
2019-02-18T17:48:58.992+0800: 365573.989: Total time for which application threads were stopped: 0.0076556 seconds, Stopping threads took: 0.0005358 seconds
2019-02-18T17:48:59.015+0800: 365574.011: Total time for which application threads were stopped: 0.0076750 seconds, Stopping threads took: 0.0005602 seconds
2019-02-18T17:48:59.026+0800: 365574.022: Total time for which application threads were stopped: 0.0089086 seconds, Stopping threads took: 0.0006000 seconds
2019-02-18T17:48:59.044+0800: 365574.041: Total time for which application threads were stopped: 0.0087554 seconds, Stopping threads took: 0.0006332 seconds
2019-02-18T17:48:59.054+0800: 365574.050: Total time for which application threads were stopped: 0.0084038 seconds, Stopping threads took: 0.0004326 seconds
{Heap before GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 48321106K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 6 survivors (196608K)
Metaspace used 50946K, capacity 51410K, committed 52168K, reserved 53248K
从日志看,期望survivor区有大概256MB,实际只有32*5=160MB,可能会将eden区直接提到old区。

近期gc时间占比过高,到40%了。

清理young 区的gc线程耗时过长,清理后可回收内存低于10%,mixed gc结束,其他gc线程空闲。说明mixed gc触发阈值设置低了,young gc参数也需要调整。

看暂停时间大于2秒的其他日志,都是触发mixed gc,但只有一个清理young和个别old区region的线程工作。确实说明mixed gc触发阈值设置低了,young gc过大了。
 
 


有41次gc的系统时间大于用户时间,大概3小时一次。

操作系统层面有可能是操作系统层异常,或者系统太忙了。出现频率大概3小时一次,看ganglia日志问题出现时cpu负载并不高。并且系统时间只是略高于用户时间不是明显有问题的数量级差距。先不考虑操作系统和JVM有bug的情况,暂不处理。

 

还是说有5秒的gc,同上。


修改参数-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UnlockExperimentalVMOptions
-XX:+ParallelRefProcEnabled
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=16
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=20
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=32m
-XX:G1MixedGCCountTarget=16
-XX:InitiatingHeapOccupancyPercent=60
-XX:MaxDirectMemorySize=110g
-XX:G1OldCSetRegionThresholdPercent=10
-XX:G1HeapWastePercent=10
-Xmx80g
-Xms80g

从gc前后堆内存图来看,-XX:InitiatingHeapOccupancyPercent=65,52GB更合适。

暂停时间平均135ms,从暂停时间图来看,young gc时间较长,需要调小young gc区,小米经验改初始值,由5改成2。-XX:G1NewSizePercent=2

mixed gc 大多数一轮不到8次,暂停时间基本都超过100ms了。期待每轮16次的参数没起作用,这是由于每轮清理的region数过多。-XX:G1OldCSetRegionThresholdPercent=5,由10改成5,减少单次mixed gc时间。

survivor区应该大于young日志中期待的,Set SurvivorRatio from 8 (default) to 4。-XX:InitiatingHeapOccupancyPercent=65
-XX:G1NewSizePercent=2
-XX:G1OldCSetRegionThresholdPercent=5
-XX:SurvivorRatio=4


设置其他必要参数

参考各类优化资料,增加以下参数-XX:+UseStringDeduplication 字符串去重,提高性能
-XX:-ResizePLAB 减少gc线程间通信的东西,关闭动态提升本地buffer
-XX:+PerfDisableSharedMem 关掉统计信息的内存映射。开启在某些特殊场景下,会极大增加gc暂停时间
优化结果




平均暂停时间,最大暂停时间,暂停时间分布,gc吞吐占比都有提高,优化成功。

接下来迭代提高这台服务器region数,做gc优化。

 
遗留问题

偶发系统时间大于用户时间的问题,依然存在。发生时系统负载并不高,有可能操作系统和JVM层面有优化空间。

gceasy建议不要用-XX:+UseGCLogFileRotation,理由是历史会看不到,文件顺序不容易分辨,多个文件不利于gc分析。但这个参数意义是控制总文件大小,所以依然保留。

附录
g1官方介绍

https://www.oracle.com/technet ... .html

其他可视化工具介绍

https://blog.csdn.net/qq_32447 ... 67984

https://blog.csdn.net/xuelinme ... 90115

https://blog.csdn.net/renfufei ... 78064

gceasy优化建议

https://blog.gceasy.io/2016/11 ... uses/

https://blog.gceasy.io/2016/12 ... time/

https://blog.gceasy.io/2016/12 ... pped/

https://blog.gceasy.io/2018/12 ... tion/

https://blog.gceasy.io/2019/01 ... tion/

优化参考

https://www.oracle.com/technet ... .html

https://blogs.apache.org/hbase ... hbase

https://software.intel.com/en- ... hbase

http://openinx.github.io/ppt/h ... 8.pdf

https://blog.csdn.net/songhaif ... 77612

https://blog.csdn.net/maosijun ... 62489

https://dzone.com/articles/g1g ... ags-1

http://www.evanjones.ca/jvm-mmap-pause.html

之前的优化探索
 http://www.hbase.group/article/192
 
专栏地址
 
https://zhuanlan.zhihu.com/c_178811296 查看全部
优化的最终目的是保障现有用户体验的同时,减少机器,节约成本。

为了更好的编写本文和后续文章,花费20美金。欢迎去附录链接专栏赞赏支持。

g1介绍
g1特点


g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。

g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在预期范围内。

g1适用场景

对平均响应时间,最大响应时间有严格要求的应用系统,如hbase regionserver。

优化原则
先优化业务层和应用层


系统调优是从业务到实现,从整体到局部,从架构到具体组件的。在进行gc调优之前,我们应该确保业务层和应用层已经评估优化过。业务层和应用层的优化一般来说更容易有收益,我们不能指望一个架构设计有缺陷,应用层代码有很多已知问题的系统,通过gc调优一劳永逸。

gc调优3选2原则

先来看一下衡量gc的指标有哪些。对应用吞吐量的影响(一般是gc对cpu的消耗),对延迟的影响,总内存占用(gc触发时留有内存业务可以继续,留有内存做对象拷贝碎片整理等操作,不能oom)。

GC调优3选2原则: 在吞吐量、延迟、内存占用上,我们只能选择其中两个进行调优,无法三者兼得。

hbase已有业务regionserver的调优目标

在调优之前,必须要有明确的性能优化目标, 然后找到未达到该目标的性能瓶颈。再针对瓶颈做优化。通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

hbase集群启用了group分组,重要业务有独立的regionserver分组。

重要业务regionserver的调优目标是,在满足业务延迟要求的基础上,用尽量低的成本,满足业务吞吐量的峰值需求。

也就是说,总吞吐量固定,延迟要求固定,单机cpu和内存固定,求最小机器数。

再转换一下,对单机来说,延迟指标确定,将单机吞吐在单机cpu和内存允许的范围内调整到最大。

需要说明的是,单机能承担多少吞吐,跟业务访问模型,region数,读写缓存参数,网络IO,磁盘IO都有关系。业务和hbase参数的调整应该在gc优化之前进行,网络和磁盘IO一般是应用层优化的。所以下文假设业务层和应用层已优化完毕,网络和磁盘都不是瓶颈,只聚焦在gc参数调优。

本文假设我们换算后的延迟目标是平均gc暂停时间100ms,最大暂停时间2s,gc时间占比3%以内。实际达到这个目标后,还要通过regionserver监控确定请求的延时要是否在用户用户的要求范围内。

影响延迟的因素

gc的时间占比。平均stw gc时间,频率。毛刺stw gc时间,频率。峰值stw gc时间,频率。

一般来说,regionserver应该避免full gc。

新生代越大,单次young gc时间越长,频率越低。

mixed gc受gc触发时机,gc并发线程数,预期迭代次数,每个迭代回收分片比例等多个参数影响,详见附录官方文档。

关于JVM版本

目前生产环境用1.8.0_77, 小米hbase环境用1.8.0_111, Oracle jdk的8最新版本是8u201。

intel性能测试见附录,jdk7不同版本间g1性能差距很大。Jdk7u21升级到jdk7u60,gc以及stw gc的平均时间,最大时间,时间分布都有大幅优化。

所以应该尽量用最新版本的JDK。

优化步骤

需要有方法论判断当前是否应该继续优化。

根据业务对延迟的需求,比较现在的请求延迟和gc情况,预估能接受的平均gc暂停时间,最大gc 暂停时间范围。

关掉自动balance,给一台regionserver少量增加region从而增加单机吞吐。当请求延迟超过用户要求的警戒线后,分析gc日志,找到瓶颈,优化降低gc延迟从而降低请求延迟,以便继续增加region。

当单机region数过多(可以考虑合并region),cpu负载过高,请求延迟无法降下来,任意一个条件满足,单机优化结束。稳定运行一段时间后,尝试将优化推广到整个业务分组。

增加日志


要分析gc情况一定要有gc日志。之前的日志参数如下
-XX:+PrintGCDetails gc细节
-XX:+PrintGCDateStamps 时间戳
-Xloggc:${HBASE_LOG_DIR}/gc-`date +'%Y%m%d%H%M'` gc文件格式
-XX:+UseGCLogFileRotation gc文件循环
-XX:NumberOfGCLogFiles=10 文件数
-XX:GCLogFileSize=512M 文件大小
-XX:+HeapDumpOnOutOfMemoryError oom时堆dump
-XX:HeapDumpPath=${HBASE_LOG_DIR}/hbase.heapdump dump目录
-XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
-XX:+PrintAdaptiveSizePolicy 打印自适应收集的大小
-XX:+PrintFlagsFinal 打印参数值

参考其他优化的文章,增加打印参数
-XX:+PrintGCApplicationStoppedTime    打印垃圾回收期间程序暂停的时间
-XX:+PrintTenuringDistribution https://www.jianshu.com/p/e634955f3bbb survivor分布情况
-XX:+PrintHeapAtGC gc前后打印堆信息
-XX:+PrintSafepointStatistics https://blog.csdn.net/u0119182 ... 47159 分析安全点统计信息,优化gc参考
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1 打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks。似乎cms更有用

gc日志太多可能会影响性能,目前没有日志对性能影响的数据,暂不考虑日志对性能的影响。


可视化

有很多gc可视化的工具。比如在线的gceasy https://gceasy.io/index.jsp#banner,上传gc日志可以进行分析。免费功能有各种图表展示。20美金一个月可以使用高级功能。

本文用gceasy做例子,其他可视化工具介绍见附录。

下面从前文优化过的节点开始,分析gc日志。








 
问题分析
gceasy优化建议


不花钱怎么变强?开启gceasy高级功能。


单次gc暂停最长到5秒了。链接给出了优化建议,见附录。

首先是优化程序降低对象创建速度。现在平均对象创建速度是1.22gb/sec。减少对象创建速度主要是更多用堆外内存,优化写放大,本文重点是gc优化,不展开。

再是年轻代太小,增大年轻代。这规则适用于其他gc,而g1 young gc是动态的,mixed gc是分次迭代的,young gc不能太大,否则young gc反而会比单次mixed慢。

再就是设置期待的最大暂停时间,目前设置100ms,暂不修改。

服务器上进程多,内存交换,服务器无这个情况。

gc线程不够多,有可能。但gc3秒以上的只有6次,差不多每天一次。如果是线程数明显不够次数应该更多,先放放。

服务器高IO导致的等待时间长。有可能,有datanode,需要进一步分析gc日志和IO监控。

没有禁用system.gc()。gc case表格来看没有相关调用。

堆太大。有可能,毕竟80GB堆。但大堆对吞吐有好处。

看5秒暂停gc的具体日志,
{Heap before GC invocations=50103 (full 0):
garbage-first heap total 83886080K, used 48760053K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 5 survivors (163840K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
2019-02-18T17:48:53.570+0800: 365568.567: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 268435456 bytes, new threshold 1 (max 1)
- age 1: 87997400 bytes, 87997400 total
365568.567: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 3313913, predicted base time: 1151.36 ms, remaining time: 0.00 ms, target pause time: 100.00 ms]
365568.567: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 123 regions, survivors: 5 regions, predicted young region time: 50.17 ms]
365568.583: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 70 regions, max: 256 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
365568.583: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 70 regions, expensive: 70 regions, min: 80 regions, remaining time: 0.00 ms]
365568.583: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 123 regions, survivors: 5 regions, old: 70 regions, predicted pause time: 4353.85 ms, target pause time: 100.00 ms]
365573.866: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 40.96 %, threshold: 10.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
365573.866: [G1Ergonomics (Mixed GCs) do not continue mixed GCs, reason: reclaimable percentage not over threshold, candidate old regions: 407 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
, 5.2999017 secs]
[Parallel Time: 5227.7 ms, GC Workers: 16]
[GC Worker Start (ms): Min: 365568584.3, Avg: 365568584.3, Max: 365568584.4, Diff: 0.1]
[Ext Root Scanning (ms): Min: 1.3, Avg: 1.6, Max: 3.9, Diff: 2.7, Sum: 25.1]
[Update RS (ms): Min: 1065.2, Avg: 1067.2, Max: 1067.8, Diff: 2.6, Sum: 17075.4]
[Processed Buffers: Min: 739, Avg: 831.5, Max: 879, Diff: 140, Sum: 13304]
[Scan RS (ms): Min: 3610.8, Avg: 3611.6, Max: 3612.6, Diff: 1.8, Sum: 57786.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.3, Diff: 0.3, Sum: 0.5]
[Object Copy (ms): Min: 545.2, Avg: 546.3, Max: 547.2, Diff: 2.0, Sum: 8741.5]
[Termination (ms): Min: 0.1, Avg: 0.6, Max: 0.8, Diff: 0.7, Sum: 9.8]
[Termination Attempts: Min: 1, Avg: 465.3, Max: 552, Diff: 551, Sum: 7445]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 5227.3, Avg: 5227.4, Max: 5227.5, Diff: 0.2, Sum: 83638.8]
[GC Worker End (ms): Min: 365573811.8, Avg: 365573811.8, Max: 365573811.8, Diff: 0.0]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 3.4 ms]
[Other: 68.4 ms]
[Choose CSet: 16.3 ms]
[Ref Proc: 3.6 ms]
[Ref Enq: 0.2 ms]
[Redirty Cards: 8.7 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 31.6 ms]
[Eden: 3936.0M(3936.0M)->0.0B(3904.0M) Survivors: 160.0M->192.0M Heap: 46.5G(80.0G)->42.3G(80.0G)]
Heap after GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 44323410K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 6 young (196608K), 6 survivors (196608K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
}
[Times: user=85.30 sys=0.00, real=5.30 secs]
2019-02-18T17:48:58.871+0800: 365573.867: Total time for which application threads were stopped: 5.3131110 seconds, Stopping threads took: 0.0005771 seconds
2019-02-18T17:48:58.896+0800: 365573.893: Total time for which application threads were stopped: 0.0186168 seconds, Stopping threads took: 0.0005541 seconds
2019-02-18T17:48:58.906+0800: 365573.902: Total time for which application threads were stopped: 0.0078098 seconds, Stopping threads took: 0.0005988 seconds
2019-02-18T17:48:58.915+0800: 365573.912: Total time for which application threads were stopped: 0.0075271 seconds, Stopping threads took: 0.0003996 seconds
2019-02-18T17:48:58.923+0800: 365573.919: Total time for which application threads were stopped: 0.0072014 seconds, Stopping threads took: 0.0003114 seconds
2019-02-18T17:48:58.934+0800: 365573.930: Total time for which application threads were stopped: 0.0078706 seconds, Stopping threads took: 0.0005490 seconds
2019-02-18T17:48:58.942+0800: 365573.938: Total time for which application threads were stopped: 0.0073198 seconds, Stopping threads took: 0.0003927 seconds
2019-02-18T17:48:58.964+0800: 365573.960: Total time for which application threads were stopped: 0.0078810 seconds, Stopping threads took: 0.0007641 seconds
2019-02-18T17:48:58.972+0800: 365573.969: Total time for which application threads were stopped: 0.0075881 seconds, Stopping threads took: 0.0006277 seconds
2019-02-18T17:48:58.981+0800: 365573.978: Total time for which application threads were stopped: 0.0081290 seconds, Stopping threads took: 0.0011246 seconds
2019-02-18T17:48:58.992+0800: 365573.989: Total time for which application threads were stopped: 0.0076556 seconds, Stopping threads took: 0.0005358 seconds
2019-02-18T17:48:59.015+0800: 365574.011: Total time for which application threads were stopped: 0.0076750 seconds, Stopping threads took: 0.0005602 seconds
2019-02-18T17:48:59.026+0800: 365574.022: Total time for which application threads were stopped: 0.0089086 seconds, Stopping threads took: 0.0006000 seconds
2019-02-18T17:48:59.044+0800: 365574.041: Total time for which application threads were stopped: 0.0087554 seconds, Stopping threads took: 0.0006332 seconds
2019-02-18T17:48:59.054+0800: 365574.050: Total time for which application threads were stopped: 0.0084038 seconds, Stopping threads took: 0.0004326 seconds
{Heap before GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 48321106K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 6 survivors (196608K)
Metaspace used 50946K, capacity 51410K, committed 52168K, reserved 53248K

从日志看,期望survivor区有大概256MB,实际只有32*5=160MB,可能会将eden区直接提到old区。

近期gc时间占比过高,到40%了。

清理young 区的gc线程耗时过长,清理后可回收内存低于10%,mixed gc结束,其他gc线程空闲。说明mixed gc触发阈值设置低了,young gc参数也需要调整。

看暂停时间大于2秒的其他日志,都是触发mixed gc,但只有一个清理young和个别old区region的线程工作。确实说明mixed gc触发阈值设置低了,young gc过大了。
 
 


有41次gc的系统时间大于用户时间,大概3小时一次。

操作系统层面有可能是操作系统层异常,或者系统太忙了。出现频率大概3小时一次,看ganglia日志问题出现时cpu负载并不高。并且系统时间只是略高于用户时间不是明显有问题的数量级差距。先不考虑操作系统和JVM有bug的情况,暂不处理。

 

还是说有5秒的gc,同上。


修改参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UnlockExperimentalVMOptions
-XX:+ParallelRefProcEnabled
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=16
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=20
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=32m
-XX:G1MixedGCCountTarget=16
-XX:InitiatingHeapOccupancyPercent=60
-XX:MaxDirectMemorySize=110g
-XX:G1OldCSetRegionThresholdPercent=10
-XX:G1HeapWastePercent=10
-Xmx80g
-Xms80g


从gc前后堆内存图来看,-XX:InitiatingHeapOccupancyPercent=65,52GB更合适。

暂停时间平均135ms,从暂停时间图来看,young gc时间较长,需要调小young gc区,小米经验改初始值,由5改成2。-XX:G1NewSizePercent=2

mixed gc 大多数一轮不到8次,暂停时间基本都超过100ms了。期待每轮16次的参数没起作用,这是由于每轮清理的region数过多。-XX:G1OldCSetRegionThresholdPercent=5,由10改成5,减少单次mixed gc时间。

survivor区应该大于young日志中期待的,Set SurvivorRatio from 8 (default) to 4。
-XX:InitiatingHeapOccupancyPercent=65
-XX:G1NewSizePercent=2
-XX:G1OldCSetRegionThresholdPercent=5
-XX:SurvivorRatio=4



设置其他必要参数

参考各类优化资料,增加以下参数
-XX:+UseStringDeduplication 字符串去重,提高性能
-XX:-ResizePLAB 减少gc线程间通信的东西,关闭动态提升本地buffer
-XX:+PerfDisableSharedMem 关掉统计信息的内存映射。开启在某些特殊场景下,会极大增加gc暂停时间

优化结果




平均暂停时间,最大暂停时间,暂停时间分布,gc吞吐占比都有提高,优化成功。

接下来迭代提高这台服务器region数,做gc优化。

 
遗留问题

偶发系统时间大于用户时间的问题,依然存在。发生时系统负载并不高,有可能操作系统和JVM层面有优化空间。

gceasy建议不要用-XX:+UseGCLogFileRotation,理由是历史会看不到,文件顺序不容易分辨,多个文件不利于gc分析。但这个参数意义是控制总文件大小,所以依然保留。

附录
g1官方介绍


https://www.oracle.com/technet ... .html

其他可视化工具介绍

https://blog.csdn.net/qq_32447 ... 67984

https://blog.csdn.net/xuelinme ... 90115

https://blog.csdn.net/renfufei ... 78064

gceasy优化建议

https://blog.gceasy.io/2016/11 ... uses/

https://blog.gceasy.io/2016/12 ... time/

https://blog.gceasy.io/2016/12 ... pped/

https://blog.gceasy.io/2018/12 ... tion/

https://blog.gceasy.io/2019/01 ... tion/

优化参考

https://www.oracle.com/technet ... .html

https://blogs.apache.org/hbase ... hbase

https://software.intel.com/en- ... hbase

http://openinx.github.io/ppt/h ... 8.pdf

https://blog.csdn.net/songhaif ... 77612

https://blog.csdn.net/maosijun ... 62489

https://dzone.com/articles/g1g ... ags-1

http://www.evanjones.ca/jvm-mmap-pause.html

之前的优化探索
 http://www.hbase.group/article/192
 
专栏地址
 
https://zhuanlan.zhihu.com/c_178811296

HBase Major Compaction没有清除Delete标志

hbasestigahuang 回复了问题 • 5 人关注 • 3 个回复 • 771 次浏览 • 2018-11-20 10:37 • 来自相关话题

HBase 生态(多模式)

hbase过往记忆 发表了文章 • 0 个评论 • 1130 次浏览 • 2018-11-07 15:18 • 来自相关话题

经过近十年的发展,目前 HBase 已经支持了许多组件用于解决不同场景的问题,这组件包括:
Phoenix : SQL on HBaseSpark : OLAP、Streaming、SQLSolr:二级索引、全文索引OpenTSDB:时序HGraphDB:图GeoMesa:时空 查看全部
经过近十年的发展,目前 HBase 已经支持了许多组件用于解决不同场景的问题,这组件包括:
  1. Phoenix : SQL on HBase
  2. Spark : OLAP、Streaming、SQL
  3. Solr:二级索引、全文索引
  4. OpenTSDB:时序
  5. HGraphDB:图
  6. GeoMesa:时空

HBase在滴滴出行的应用场景和最佳实践

hbase过往记忆 发表了文章 • 1 个评论 • 574 次浏览 • 2018-10-23 10:27 • 来自相关话题

作者简介:李扬,滴滴出行资深软件开发工程师。2015年加入滴滴出行基础平台部,主要负责HBase和Phoenix以及相关分布式存储技术。在滴滴之前,曾在新浪担任数据工程师,专注于分布式计算和存储。 
责编:郭芮(guorui@csdn.net),关注大数据领域。 
 背景
对接业务类型

HBase是建立在Hadoop生态之上的Database,源生对离线任务支持友好,又因为LSM树是一个优秀的高吞吐数据库结构,所以同时也对接了很多线上业务。在线业务对访问延迟敏感,并且访问趋向于随机,如订单、客服轨迹查询。离线业务通常是数仓的定时大批量处理任务,对一段时间内的数据进行处理并产出结果,对任务完成的时间要求不是非常敏感,并且处理逻辑复杂,如天级别报表、安全和用户行为分析、模型训练等。

多语言支持

HBase提供了多语言解决方案,并且由于滴滴各业务线RD所使用的开发语言各有偏好,所以多语言支持对于HBase在滴滴内部的发展是至关重要的一部分。我们对用户提供了多种语言的访问方式:HBase Java native API、Thrift Server(主要应用于C++、PHP、Python)、JAVA JDBC(Phoenix JDBC)、Phoenix QueryServer(Phoenix对外提供的多语言解决方案)、MapReduce Job(Htable/Hfile Input)、Spark Job、Streaming等。

数据类型

HBase在滴滴主要存放了以下四种数据类型:
 
统计结果、报表类数据:主要是运营、运力情况、收入等结果,通常需要配合Phoenix进行SQL查询。数据量较小,对查询的灵活性要求高,延迟要求一般。原始事实类数据:如订单、司机乘客的GPS轨迹、日志等,主要用作在线和离线的数据供给。数据量大,对一致性和可用性要求高,延迟敏感,实时写入,单点或批量查询。中间结果数据:指模型训练所需要的数据等。数据量大,可用性和一致性要求一般,对批量查询时的吞吐量要求高。线上系统的备份数据:用户把原始数据存在了其他关系数据库或文件服务,把HBase作为一个异地容灾的方案。
 使用场景介绍
场景一:订单事件

这份数据使用过滴滴产品的用户应该都接触过,就是App上的历史订单。近期订单的查询会落在Redis,超过一定时间范围,或者当Redis不可用时,查询会落在HBase上。业务方的需求如下:
 
在线查询订单生命周期的各个状态,包括status、event_type、order_detail等信息。主要的查询来自于客服系统。在线历史订单详情查询。上层会有Redis来存储近期的订单,当Redis不可用或者查询范围超出Redis,查询会直接落到HBase。离线对订单的状态进行分析。写入满足每秒10K的事件,读取满足每秒1K的事件,数据要求在5s内可用。





按照这些要求,我们对Rowkey做出了下面的设计,都是很典型的scan场景。

订单状态表

Rowkey:reverse(order_id) + (MAX_LONG - TS) 
Columns:该订单各种状态

订单历史表

Rowkey:reverse(passenger_id | driver_id) + (MAX_LONG - TS) 
Columns:用户在时间范围内的订单及其他信息

场景二:司机乘客轨迹

这也是一份滴滴用户关系密切的数据,线上用户、滴滴的各个业务线和分析人员都会使用。举几个使用场景上的例子:用户查看历史订单时,地图上显示所经过的路线;发生司乘纠纷,客服调用订单轨迹复现场景;地图部门用户分析道路拥堵情况。





用户们提出的需求:
 
满足App用户或者后端分析人员的实时或准实时轨迹坐标查询;满足离线大规模的轨迹分析;满足给出一个指定的地理范围,取出范围内所有用户的轨迹或范围内出现过的用户。
 其中,关于第三个需求,地理位置查询,我们知道MongoDB对于这种地理索引有源生的支持,但是在滴滴这种量级的情况下可能会发生存储瓶颈,HBase存储和扩展性上没有压力但是没有内置类似MongoDB地理位置索引的功能,没有就需要我们自己实现。通过调研,了解到关于地理索引有一套比较通用的GeohHash算法 。

GeoHash是将二维的经纬度转换成字符串,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,比如说我在悠唐酒店,我的一个朋友在旁边的悠唐购物广场,我们的经纬度点会得到相同的GeoHash串。这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。





但是我们要查询的范围和GeohHash块可能不会完全重合。以圆形为例,查询时会出现如图4所示的一半在GeoHash块内,一半在外面的情况(如A、B、C、D、E、F、G等点)。这种情况就需要对GeoHash块内每个真实的GPS点进行第二次的过滤,通过原始的GPS点和圆心之间的距离,过滤掉不符合查询条件的数据。





最后依据这个原理,把GeoHash和其他一些需要被索引的维度拼装成Rowkey,真实的GPS点为Value,在这个基础上封装成客户端,并且在客户端内部对查询逻辑和查询策略做出速度上的大幅优化,这样就把HBase变成了一个MongoDB一样支持地理位置索引的数据库。如果查询范围非常大(比如进行省级别的分析),还额外提供了MR的获取数据的入口。

两种查询场景的Rowkey设计如下:
 
单个用户按订单或时间段查询: reverse(user_id) + (Integer.MAX_LONG-TS/1000)给定范围内的轨迹查询:reverse(geohash) + ts/1000 + user_id
 场景三:ETA

ETA是指每次选好起始和目的地后,提示出的预估时间和价格。提示的预估到达时间和价格,最初版本是离线方式运行,后来改版通过HBase实现实时效果,把HBase当成一个KeyValue缓存,带来了减少训练时间、可多城市并行、减少人工干预的好处。 
整个ETA的过程如下:
 
模型训练通过Spark Job,每30分钟对各个城市训练一次;模型训练第一阶段,在5分钟内,按照设定条件从HBase读取所有城市数据;模型训练第二阶段在25分钟内完成ETA的计算;HBase中的数据每隔一段时间会持久化至HDFS中,供新模型测试和新的特征提取。
 Rowkey:salting+cited+type0+type1+type2+TS 
Column:order, feature





场景四:监控工具DCM

用于监控Hadoop集群的资源使用(Namenode,Yarn container使用等),关系数据库在时间维度过程以后会产生各种性能问题,同时我们又希望可以通过SQL做一些分析查询,所以使用Phoenix,使用采集程序定时录入数据,生产成报表,存入HBase,可以在秒级别返回查询结果,最后在前端做展示。





图7、图8、图9是几张监控工具的用户UI,数字相关的部分做了模糊处理。















滴滴在HBase对多租户的管理
我们认为单集群多租户是最高效和节省精力的方案,但是由于HBase对多租户基本没有管理,使用上会遇到很多问题:在用户方面比如对资源使用情况不做分析、存储总量发生变化后不做调整和通知、项目上线下线没有计划、想要最多的资源和权限等;我们平台管理者也会遇到比如线上沟通难以理解用户的业务、对每个接入HBase的项目状态不清楚、不能判断出用户的需求是否合理、多租户在集群上发生资源竞争、问题定位和排查时间长等。

针对这些问题,我们开发了DHS系统(Didi HBase Service)进行项目管理,并且在HBase上通过Namespace、RS Group等技术来分割用户的资源、数据和权限。通过计算开销并计费的方法来管控资源分配。





DHS主要有下面几个模块和功能:
 
项目生命周期管理:包括立项、资源预估和申请、项目需求调整、需求讨论;用户管理:权限管理,项目审批;集群资源管理;表级别的使用情况监控:主要是读写监控、memstore、blockcache、locality。
 当用户有使用HBase存储的需求,我们会让用户在DHS上注册项目。介绍业务的场景和产品相关的细节,以及是否有高SLA要求。

之后是新建表以及对表性能需求预估,我们要求用户对自己要使用的资源有一个准确的预估。如果用户难以估计,我们会以线上或者线下讨论的方式与用户讨论帮助确定这些信息。 
然后会生成项目概览页面,方便管理员和用户进行项目进展的跟踪。

HBase自带的jxm信息会汇总到Region和RegionServer级别的数据,管理员会经常用到,但是用户却很少关注这个级别。根据这种情况我们开发了HBase表级别的监控,并且会有权限控制,让业务RD只能看到和自己相关的表,清楚自己项目表的吞吐及存储占用情况。

通过DHS让用户明确自己使用资源情况的基础之上,我们使用了RS Group技术,把一个集群分成多个逻辑子集群,可以让用户选择独占或者共享资源。共享和独占各有自己的优缺点,如表1。





根据以上的情况,我们在资源分配上会根据业务的特性来选择不同方案:
 
对于访问延迟要求低、访问量小、可用性要求低、备份或者测试阶段的数据:使用共享资源池;对于延迟敏感、吞吐要求高、高峰时段访问量大、可用性要求高、在线业务:让其独占一定机器数量构成的RegionServer Group资源,并且按用户预估的资源量,额外给出20%~30%的余量。
 最后我们会根据用户对资源的使用,定期计算开销并向用户发出账单。

RS Group
RegionServer Group,实现细节可以参照HBase HBASE-6721这个Patch。滴滴在这个基础上作了一些分配策略上的优化,以便适合滴滴业务场景的修改。RS Group简单概括是指通过分配一批指定的RegionServer列表,成为一个RS Group,每个Group可以按需挂载不同的表,并且当Group内的表发生异常后,Region不会迁移到其他的Group。这样,每个Group就相当于一个逻辑上的子集群,通过这种方式达到资源隔离的效果,降低管理成本,不必为每个高SLA的业务线单独搭集群。





总结
在滴滴推广和实践HBase的工作中,我们认为至关重要的两点是帮助用户做出良好的表结构设计和资源的控制。有了这两个前提之后,后续出现问题的概率会大大降低。良好的表结构设计需要用户对HBase的实现有一个清晰的认识,大多数业务用户把更多精力放在了业务逻辑上,对架构实现知之甚少,这就需要平台管理者去不断帮助和引导,有了好的开端和成功案例后,通过这些用户再去向其他的业务方推广。资源隔离控制则帮助我们有效减少集群的数量,降低运维成本,让平台管理者从多集群无止尽的管理工作中解放出来,将更多精力投入到组件社区跟进和平台管理系统的研发工作中,使业务和平台都进入一个良性循环,提升用户的使用体验,更好地支持公司业务的发展。
  查看全部
作者简介:李扬,滴滴出行资深软件开发工程师。2015年加入滴滴出行基础平台部,主要负责HBase和Phoenix以及相关分布式存储技术。在滴滴之前,曾在新浪担任数据工程师,专注于分布式计算和存储。 
责编:郭芮(guorui@csdn.net),关注大数据领域。 
 背景
对接业务类型

HBase是建立在Hadoop生态之上的Database,源生对离线任务支持友好,又因为LSM树是一个优秀的高吞吐数据库结构,所以同时也对接了很多线上业务。在线业务对访问延迟敏感,并且访问趋向于随机,如订单、客服轨迹查询。离线业务通常是数仓的定时大批量处理任务,对一段时间内的数据进行处理并产出结果,对任务完成的时间要求不是非常敏感,并且处理逻辑复杂,如天级别报表、安全和用户行为分析、模型训练等。

多语言支持

HBase提供了多语言解决方案,并且由于滴滴各业务线RD所使用的开发语言各有偏好,所以多语言支持对于HBase在滴滴内部的发展是至关重要的一部分。我们对用户提供了多种语言的访问方式:HBase Java native API、Thrift Server(主要应用于C++、PHP、Python)、JAVA JDBC(Phoenix JDBC)、Phoenix QueryServer(Phoenix对外提供的多语言解决方案)、MapReduce Job(Htable/Hfile Input)、Spark Job、Streaming等。

数据类型

HBase在滴滴主要存放了以下四种数据类型:
 
  • 统计结果、报表类数据:主要是运营、运力情况、收入等结果,通常需要配合Phoenix进行SQL查询。数据量较小,对查询的灵活性要求高,延迟要求一般。
  • 原始事实类数据:如订单、司机乘客的GPS轨迹、日志等,主要用作在线和离线的数据供给。数据量大,对一致性和可用性要求高,延迟敏感,实时写入,单点或批量查询。
  • 中间结果数据:指模型训练所需要的数据等。数据量大,可用性和一致性要求一般,对批量查询时的吞吐量要求高。
  • 线上系统的备份数据:用户把原始数据存在了其他关系数据库或文件服务,把HBase作为一个异地容灾的方案。

 使用场景介绍
场景一:订单事件

这份数据使用过滴滴产品的用户应该都接触过,就是App上的历史订单。近期订单的查询会落在Redis,超过一定时间范围,或者当Redis不可用时,查询会落在HBase上。业务方的需求如下:
 
  • 在线查询订单生命周期的各个状态,包括status、event_type、order_detail等信息。主要的查询来自于客服系统。
  • 在线历史订单详情查询。上层会有Redis来存储近期的订单,当Redis不可用或者查询范围超出Redis,查询会直接落到HBase。
  • 离线对订单的状态进行分析。
  • 写入满足每秒10K的事件,读取满足每秒1K的事件,数据要求在5s内可用。


592fd58ff297e.png

按照这些要求,我们对Rowkey做出了下面的设计,都是很典型的scan场景。

订单状态表

Rowkey:reverse(order_id) + (MAX_LONG - TS) 
Columns:该订单各种状态

订单历史表

Rowkey:reverse(passenger_id | driver_id) + (MAX_LONG - TS) 
Columns:用户在时间范围内的订单及其他信息

场景二:司机乘客轨迹

这也是一份滴滴用户关系密切的数据,线上用户、滴滴的各个业务线和分析人员都会使用。举几个使用场景上的例子:用户查看历史订单时,地图上显示所经过的路线;发生司乘纠纷,客服调用订单轨迹复现场景;地图部门用户分析道路拥堵情况。

592fd62ec3aa9.png

用户们提出的需求:
 
  • 满足App用户或者后端分析人员的实时或准实时轨迹坐标查询;
  • 满足离线大规模的轨迹分析;
  • 满足给出一个指定的地理范围,取出范围内所有用户的轨迹或范围内出现过的用户。

 其中,关于第三个需求,地理位置查询,我们知道MongoDB对于这种地理索引有源生的支持,但是在滴滴这种量级的情况下可能会发生存储瓶颈,HBase存储和扩展性上没有压力但是没有内置类似MongoDB地理位置索引的功能,没有就需要我们自己实现。通过调研,了解到关于地理索引有一套比较通用的GeohHash算法 。

GeoHash是将二维的经纬度转换成字符串,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,比如说我在悠唐酒店,我的一个朋友在旁边的悠唐购物广场,我们的经纬度点会得到相同的GeoHash串。这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。

592fd6715b13f.png

但是我们要查询的范围和GeohHash块可能不会完全重合。以圆形为例,查询时会出现如图4所示的一半在GeoHash块内,一半在外面的情况(如A、B、C、D、E、F、G等点)。这种情况就需要对GeoHash块内每个真实的GPS点进行第二次的过滤,通过原始的GPS点和圆心之间的距离,过滤掉不符合查询条件的数据。

592fd6ba65517.png

最后依据这个原理,把GeoHash和其他一些需要被索引的维度拼装成Rowkey,真实的GPS点为Value,在这个基础上封装成客户端,并且在客户端内部对查询逻辑和查询策略做出速度上的大幅优化,这样就把HBase变成了一个MongoDB一样支持地理位置索引的数据库。如果查询范围非常大(比如进行省级别的分析),还额外提供了MR的获取数据的入口。

两种查询场景的Rowkey设计如下:
 
  • 单个用户按订单或时间段查询: reverse(user_id) + (Integer.MAX_LONG-TS/1000)
  • 给定范围内的轨迹查询:reverse(geohash) + ts/1000 + user_id

 场景三:ETA

ETA是指每次选好起始和目的地后,提示出的预估时间和价格。提示的预估到达时间和价格,最初版本是离线方式运行,后来改版通过HBase实现实时效果,把HBase当成一个KeyValue缓存,带来了减少训练时间、可多城市并行、减少人工干预的好处。 
整个ETA的过程如下:
 
  • 模型训练通过Spark Job,每30分钟对各个城市训练一次;
  • 模型训练第一阶段,在5分钟内,按照设定条件从HBase读取所有城市数据;
  • 模型训练第二阶段在25分钟内完成ETA的计算;
  • HBase中的数据每隔一段时间会持久化至HDFS中,供新模型测试和新的特征提取。

 Rowkey:salting+cited+type0+type1+type2+TS 
Column:order, feature

592fd708c4246.png

场景四:监控工具DCM

用于监控Hadoop集群的资源使用(Namenode,Yarn container使用等),关系数据库在时间维度过程以后会产生各种性能问题,同时我们又希望可以通过SQL做一些分析查询,所以使用Phoenix,使用采集程序定时录入数据,生产成报表,存入HBase,可以在秒级别返回查询结果,最后在前端做展示。

592fd7375c265.png

图7、图8、图9是几张监控工具的用户UI,数字相关的部分做了模糊处理。

592fd7610405d.png


592fd77d8ebbe.png


592fd7997641f.png

滴滴在HBase对多租户的管理
我们认为单集群多租户是最高效和节省精力的方案,但是由于HBase对多租户基本没有管理,使用上会遇到很多问题:在用户方面比如对资源使用情况不做分析、存储总量发生变化后不做调整和通知、项目上线下线没有计划、想要最多的资源和权限等;我们平台管理者也会遇到比如线上沟通难以理解用户的业务、对每个接入HBase的项目状态不清楚、不能判断出用户的需求是否合理、多租户在集群上发生资源竞争、问题定位和排查时间长等。

针对这些问题,我们开发了DHS系统(Didi HBase Service)进行项目管理,并且在HBase上通过Namespace、RS Group等技术来分割用户的资源、数据和权限。通过计算开销并计费的方法来管控资源分配。

592fd7f8d4da8.png

DHS主要有下面几个模块和功能:
 
  • 项目生命周期管理:包括立项、资源预估和申请、项目需求调整、需求讨论;
  • 用户管理:权限管理,项目审批;
  • 集群资源管理;
  • 表级别的使用情况监控:主要是读写监控、memstore、blockcache、locality。

 当用户有使用HBase存储的需求,我们会让用户在DHS上注册项目。介绍业务的场景和产品相关的细节,以及是否有高SLA要求。

之后是新建表以及对表性能需求预估,我们要求用户对自己要使用的资源有一个准确的预估。如果用户难以估计,我们会以线上或者线下讨论的方式与用户讨论帮助确定这些信息。 
然后会生成项目概览页面,方便管理员和用户进行项目进展的跟踪。

HBase自带的jxm信息会汇总到Region和RegionServer级别的数据,管理员会经常用到,但是用户却很少关注这个级别。根据这种情况我们开发了HBase表级别的监控,并且会有权限控制,让业务RD只能看到和自己相关的表,清楚自己项目表的吞吐及存储占用情况。

通过DHS让用户明确自己使用资源情况的基础之上,我们使用了RS Group技术,把一个集群分成多个逻辑子集群,可以让用户选择独占或者共享资源。共享和独占各有自己的优缺点,如表1。

592fd853ebaac.png

根据以上的情况,我们在资源分配上会根据业务的特性来选择不同方案:
 
  • 对于访问延迟要求低、访问量小、可用性要求低、备份或者测试阶段的数据:使用共享资源池;
  • 对于延迟敏感、吞吐要求高、高峰时段访问量大、可用性要求高、在线业务:让其独占一定机器数量构成的RegionServer Group资源,并且按用户预估的资源量,额外给出20%~30%的余量。

 最后我们会根据用户对资源的使用,定期计算开销并向用户发出账单。

RS Group
RegionServer Group,实现细节可以参照HBase HBASE-6721这个Patch。滴滴在这个基础上作了一些分配策略上的优化,以便适合滴滴业务场景的修改。RS Group简单概括是指通过分配一批指定的RegionServer列表,成为一个RS Group,每个Group可以按需挂载不同的表,并且当Group内的表发生异常后,Region不会迁移到其他的Group。这样,每个Group就相当于一个逻辑上的子集群,通过这种方式达到资源隔离的效果,降低管理成本,不必为每个高SLA的业务线单独搭集群。

592fd8a900ab5.png

总结
在滴滴推广和实践HBase的工作中,我们认为至关重要的两点是帮助用户做出良好的表结构设计和资源的控制。有了这两个前提之后,后续出现问题的概率会大大降低。良好的表结构设计需要用户对HBase的实现有一个清晰的认识,大多数业务用户把更多精力放在了业务逻辑上,对架构实现知之甚少,这就需要平台管理者去不断帮助和引导,有了好的开端和成功案例后,通过这些用户再去向其他的业务方推广。资源隔离控制则帮助我们有效减少集群的数量,降低运维成本,让平台管理者从多集群无止尽的管理工作中解放出来,将更多精力投入到组件社区跟进和平台管理系统的研发工作中,使业务和平台都进入一个良性循环,提升用户的使用体验,更好地支持公司业务的发展。
 

连接HBase的正确姿势

hbasehbasegroup 发表了文章 • 1 个评论 • 1767 次浏览 • 2018-10-01 17:42 • 来自相关话题

在云HBase值班的时候,经常会遇见有用户咨询诸如“HBase是否支持连接池?”这样的问题,也有用户因为应用中创建的Connection对象过多,触发Zookeeper的连接数限制,导致客户端连不上的。究其原因,都是因为对HBase客户端的原理不了解造成的。本文介绍HBase客户端的Connection对象与Socket连接的关系并且给出Connection的正确用法。
Connection是什么?
在云HBase用户中,常见的使用Connection的错误方法有:
自己实现一个Connection对象的资源池,每次使用都从资源池中取出一个Connection对象;每个线程一个Connection对象。每次访问HBase的时候临时创建一个Connection对象,使用完之后调用close关闭连接。
从这些做法来看,这些用户显然是把Connection对象当成了单机数据库里面的连接对象来用了。然而作为分布式数据库,HBase客户端需要和多个服务器中的不同服务角色建立连接,所以HBase客户端中的Connection对象并不是简单对应一个socket连接。HBase的API文档当中对Connection的定义是:
A cluster connection encapsulating lower level individual connections to actual servers and a connection to zookeeper.
我们知道,HBase客户端要连接三个不同的服务角色:
Zookeeper:主要用于获得meta-region位置,集群Id、master等信息。HBase Master:主要用于执行HBaseAdmin接口的一些操作,例如建表等。HBase RegionServer:用于读、写数据。
下图简单示意了客户端与服务器交互的步骤:



HBase客户端的Connection包含了对以上三种Socket连接的封装。Connection对象和实际的Socket连接之间的对应关系如下图:



HBase客户端代码真正对应Socket连接的是RpcConnection对象。HBase使用PoolMap这种数据结构来存储客户端到HBase服务器之间的连接。PoolMap封装ConcurrentHashMap的结构,key是ConnectionId(封装服务器地址和用户ticket),value是一个RpcConnection对象的资源池。当HBase需要连接一个服务器时,首先会根据ConnectionId找到对应的连接池,然后从连接池中取出一个连接对象。
HBase提供三种资源池的实现,分别是Reusable,RoundRobin和ThreadLocal。具体实现可以通过hbase.client.ipc.pool.type配置项指定,默认为Reusable。连接池的大小也可以通过hbase.client.ipc.pool.size配置项指定,默认为1。
连接HBase的正确姿势
从以上分析不难得出,在HBase中Connection类已经实现对连接的管理功能,所以不需要在Connection之上再做额外的管理。另外,Connection是线程安全的,然而Table和Admin则不是线程安全的,因此正确的做法是一个进程共用一个Connection对象,而在不同的线程中使用单独的Table和Admin对象。//所有进程共用一个Connection对象
connection=ConnectionFactory.createConnection(config);
...
//每个线程使用单独的Table对象
Table table = connection.getTable(TableName.valueOf("test"));
try {
...
} finally {
table.close();
}HBase客户端默认的是连接池大小是1,也就是每个RegionServer 1个连接。如果应用需要使用更大的连接池或指定其他的资源池类型,也可以通过修改配置实现: 
Connection源码解析
Connection创建RpcClient的核心入口:/**
* constructor
* @param conf Configuration object
*/
ConnectionImplementation(Configuration conf,
ExecutorService pool, User user) throws IOException {
...
try {
...
this.rpcClient = RpcClientFactory.createClient(this.conf, this.clusterId, this.metrics);
...
} catch (Throwable e) {
// avoid leaks: registry, rpcClient, ...
LOG.debug("connection construction failed", e);
close();
throw e;
}
}RpcClient使用PoolMap数据结构存储客户端到HBase服务器之间的连接映射,PoolMap封装ConcurrentHashMap结构,其中key是ConnectionId[new ConnectionId(ticket, md.getService().getName(), addr)],value是RpcConnection对象的资源池。protected final PoolMap<ConnectionId, T> connections;

/**
* Construct an IPC client for the cluster <code>clusterId</code>
* @param conf configuration
* @param clusterId the cluster id
* @param localAddr client socket bind address.
* @param metrics the connection metrics
*/
public AbstractRpcClient(Configuration conf, String clusterId, SocketAddress localAddr,
MetricsConnection metrics) {
...
this.connections = new PoolMap<>(getPoolType(conf), getPoolSize(conf));
...
}当HBase需要连接一个服务器时,首先会根据ConnectionId找到对应的连接池,然后从连接池中取出一个连接对象,获取连接的核心实现:/**
* Get a connection from the pool, or create a new one and add it to the pool. Connections to a
* given host/port are reused.
*/
private T getConnection(ConnectionId remoteId) throws IOException {
if (failedServers.isFailedServer(remoteId.getAddress())) {
if (LOG.isDebugEnabled()) {
LOG.debug("Not trying to connect to " + remoteId.address
+ " this server is in the failed servers list");
}
throw new FailedServerException(
"This server is in the failed servers list: " + remoteId.address);
}
T conn;
synchronized (connections) {
if (!running) {
throw new StoppedRpcClientException();
}
conn = connections.get(remoteId);
if (conn == null) {
conn = createConnection(remoteId);
connections.put(remoteId, conn);
}
conn.setLastTouched(EnvironmentEdgeManager.currentTime());
}
return conn;
}连接池根据ConnectionId获取不到连接则创建RpcConnection的具体实现:protected NettyRpcConnection createConnection(ConnectionId remoteId) throws IOException {
return new NettyRpcConnection(this, remoteId);
}

NettyRpcConnection(NettyRpcClient rpcClient, ConnectionId remoteId) throws IOException {
super(rpcClient.conf, AbstractRpcClient.WHEEL_TIMER, remoteId, rpcClient.clusterId,
rpcClient.userProvider.isHBaseSecurityEnabled(), rpcClient.codec, rpcClient.compressor);
this.rpcClient = rpcClient;
byte connectionHeaderPreamble = getConnectionHeaderPreamble();
this.connectionHeaderPreamble =
Unpooled.directBuffer(connectionHeaderPreamble.length).writeBytes(connectionHeaderPreamble);
ConnectionHeader header = getConnectionHeader();
this.connectionHeaderWithLength = Unpooled.directBuffer(4 + header.getSerializedSize());
this.connectionHeaderWithLength.writeInt(header.getSerializedSize());
header.writeTo(new ByteBufOutputStream(this.connectionHeaderWithLength));
}

protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, ConnectionId remoteId,
String clusterId, boolean isSecurityEnabled, Codec codec, CompressionCodec compressor)
throws IOException {
if (remoteId.getAddress().isUnresolved()) {
throw new UnknownHostException("unknown host: " + remoteId.getAddress().getHostName());
}
this.timeoutTimer = timeoutTimer;
this.codec = codec;
this.compressor = compressor;
this.conf = conf;

UserGroupInformation ticket = remoteId.getTicket().getUGI();
SecurityInfo securityInfo = SecurityInfo.getInfo(remoteId.getServiceName());
this.useSasl = isSecurityEnabled;
Token<? extends TokenIdentifier> token = null;
String serverPrincipal = null;
if (useSasl && securityInfo != null) {
AuthenticationProtos.TokenIdentifier.Kind tokenKind = securityInfo.getTokenKind();
if (tokenKind != null) {
TokenSelector<? extends TokenIdentifier> tokenSelector = AbstractRpcClient.TOKEN_HANDLERS
.get(tokenKind);
if (tokenSelector != null) {
token = tokenSelector.selectToken(new Text(clusterId), ticket.getTokens());
} else if (LOG.isDebugEnabled()) {
LOG.debug("No token selector found for type " + tokenKind);
}
}
String serverKey = securityInfo.getServerPrincipal();
if (serverKey == null) {
throw new IOException("Can't obtain server Kerberos config key from SecurityInfo");
}
serverPrincipal = SecurityUtil.getServerPrincipal(conf.get(serverKey),
remoteId.address.getAddress().getCanonicalHostName().toLowerCase());
if (LOG.isDebugEnabled()) {
LOG.debug("RPC Server Kerberos principal name for service=" + remoteId.getServiceName()
+ " is " + serverPrincipal);
}
}
this.token = token;
this.serverPrincipal = serverPrincipal;
if (!useSasl) {
authMethod = AuthMethod.SIMPLE;
} else if (token != null) {
authMethod = AuthMethod.DIGEST;
} else {
authMethod = AuthMethod.KERBEROS;
}

// Log if debug AND non-default auth, else if trace enabled.
// No point logging obvious.
if ((LOG.isDebugEnabled() && !authMethod.equals(AuthMethod.SIMPLE)) ||
LOG.isTraceEnabled()) {
// Only log if not default auth.
LOG.debug("Use " + authMethod + " authentication for service " + remoteId.serviceName
+ ", sasl=" + useSasl);
}
reloginMaxBackoff = conf.getInt("hbase.security.relogin.maxbackoff", 5000);
this.remoteId = remoteId;
} 查看全部
在云HBase值班的时候,经常会遇见有用户咨询诸如“HBase是否支持连接池?”这样的问题,也有用户因为应用中创建的Connection对象过多,触发Zookeeper的连接数限制,导致客户端连不上的。究其原因,都是因为对HBase客户端的原理不了解造成的。本文介绍HBase客户端的Connection对象与Socket连接的关系并且给出Connection的正确用法。
Connection是什么?
在云HBase用户中,常见的使用Connection的错误方法有:
  1. 自己实现一个Connection对象的资源池,每次使用都从资源池中取出一个Connection对象;
  2. 每个线程一个Connection对象。
  3. 每次访问HBase的时候临时创建一个Connection对象,使用完之后调用close关闭连接。

从这些做法来看,这些用户显然是把Connection对象当成了单机数据库里面的连接对象来用了。然而作为分布式数据库,HBase客户端需要和多个服务器中的不同服务角色建立连接,所以HBase客户端中的Connection对象并不是简单对应一个socket连接。HBase的API文档当中对Connection的定义是:
A cluster connection encapsulating lower level individual connections to actual servers and a connection to zookeeper.
我们知道,HBase客户端要连接三个不同的服务角色:
  1. Zookeeper:主要用于获得meta-region位置,集群Id、master等信息。
  2. HBase Master:主要用于执行HBaseAdmin接口的一些操作,例如建表等。
  3. HBase RegionServer:用于读、写数据。

下图简单示意了客户端与服务器交互的步骤:
客户端与服务器交互的步骤.png
HBase客户端的Connection包含了对以上三种Socket连接的封装。Connection对象和实际的Socket连接之间的对应关系如下图:
Connection对象与Socket连接之间的对应关系.png
HBase客户端代码真正对应Socket连接的是RpcConnection对象。HBase使用PoolMap这种数据结构来存储客户端到HBase服务器之间的连接。PoolMap封装ConcurrentHashMap的结构,key是ConnectionId(封装服务器地址和用户ticket),value是一个RpcConnection对象的资源池。当HBase需要连接一个服务器时,首先会根据ConnectionId找到对应的连接池,然后从连接池中取出一个连接对象。
HBase提供三种资源池的实现,分别是Reusable,RoundRobin和ThreadLocal。具体实现可以通过hbase.client.ipc.pool.type配置项指定,默认为Reusable。连接池的大小也可以通过hbase.client.ipc.pool.size配置项指定,默认为1。
连接HBase的正确姿势
从以上分析不难得出,在HBase中Connection类已经实现对连接的管理功能,所以不需要在Connection之上再做额外的管理。另外,Connection是线程安全的,然而Table和Admin则不是线程安全的,因此正确的做法是一个进程共用一个Connection对象,而在不同的线程中使用单独的Table和Admin对象。
//所有进程共用一个Connection对象
connection=ConnectionFactory.createConnection(config);
...
//每个线程使用单独的Table对象
Table table = connection.getTable(TableName.valueOf("test"));
try {
...
} finally {
table.close();
}
HBase客户端默认的是连接池大小是1,也就是每个RegionServer 1个连接。如果应用需要使用更大的连接池或指定其他的资源池类型,也可以通过修改配置实现: 
Connection源码解析
Connection创建RpcClient的核心入口:
/**
* constructor
* @param conf Configuration object
*/
ConnectionImplementation(Configuration conf,
ExecutorService pool, User user) throws IOException {
...
try {
...
this.rpcClient = RpcClientFactory.createClient(this.conf, this.clusterId, this.metrics);
...
} catch (Throwable e) {
// avoid leaks: registry, rpcClient, ...
LOG.debug("connection construction failed", e);
close();
throw e;
}
}
RpcClient使用PoolMap数据结构存储客户端到HBase服务器之间的连接映射,PoolMap封装ConcurrentHashMap结构,其中key是ConnectionId[new ConnectionId(ticket, md.getService().getName(), addr)],value是RpcConnection对象的资源池。
protected final PoolMap<ConnectionId, T> connections;

/**
* Construct an IPC client for the cluster <code>clusterId</code>
* @param conf configuration
* @param clusterId the cluster id
* @param localAddr client socket bind address.
* @param metrics the connection metrics
*/
public AbstractRpcClient(Configuration conf, String clusterId, SocketAddress localAddr,
MetricsConnection metrics) {
...
this.connections = new PoolMap<>(getPoolType(conf), getPoolSize(conf));
...
}
当HBase需要连接一个服务器时,首先会根据ConnectionId找到对应的连接池,然后从连接池中取出一个连接对象,获取连接的核心实现:
/**
* Get a connection from the pool, or create a new one and add it to the pool. Connections to a
* given host/port are reused.
*/
private T getConnection(ConnectionId remoteId) throws IOException {
if (failedServers.isFailedServer(remoteId.getAddress())) {
if (LOG.isDebugEnabled()) {
LOG.debug("Not trying to connect to " + remoteId.address
+ " this server is in the failed servers list");
}
throw new FailedServerException(
"This server is in the failed servers list: " + remoteId.address);
}
T conn;
synchronized (connections) {
if (!running) {
throw new StoppedRpcClientException();
}
conn = connections.get(remoteId);
if (conn == null) {
conn = createConnection(remoteId);
connections.put(remoteId, conn);
}
conn.setLastTouched(EnvironmentEdgeManager.currentTime());
}
return conn;
}
连接池根据ConnectionId获取不到连接则创建RpcConnection的具体实现:
protected NettyRpcConnection createConnection(ConnectionId remoteId) throws IOException {
return new NettyRpcConnection(this, remoteId);
}

NettyRpcConnection(NettyRpcClient rpcClient, ConnectionId remoteId) throws IOException {
super(rpcClient.conf, AbstractRpcClient.WHEEL_TIMER, remoteId, rpcClient.clusterId,
rpcClient.userProvider.isHBaseSecurityEnabled(), rpcClient.codec, rpcClient.compressor);
this.rpcClient = rpcClient;
byte connectionHeaderPreamble = getConnectionHeaderPreamble();
this.connectionHeaderPreamble =
Unpooled.directBuffer(connectionHeaderPreamble.length).writeBytes(connectionHeaderPreamble);
ConnectionHeader header = getConnectionHeader();
this.connectionHeaderWithLength = Unpooled.directBuffer(4 + header.getSerializedSize());
this.connectionHeaderWithLength.writeInt(header.getSerializedSize());
header.writeTo(new ByteBufOutputStream(this.connectionHeaderWithLength));
}

protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, ConnectionId remoteId,
String clusterId, boolean isSecurityEnabled, Codec codec, CompressionCodec compressor)
throws IOException {
if (remoteId.getAddress().isUnresolved()) {
throw new UnknownHostException("unknown host: " + remoteId.getAddress().getHostName());
}
this.timeoutTimer = timeoutTimer;
this.codec = codec;
this.compressor = compressor;
this.conf = conf;

UserGroupInformation ticket = remoteId.getTicket().getUGI();
SecurityInfo securityInfo = SecurityInfo.getInfo(remoteId.getServiceName());
this.useSasl = isSecurityEnabled;
Token<? extends TokenIdentifier> token = null;
String serverPrincipal = null;
if (useSasl && securityInfo != null) {
AuthenticationProtos.TokenIdentifier.Kind tokenKind = securityInfo.getTokenKind();
if (tokenKind != null) {
TokenSelector<? extends TokenIdentifier> tokenSelector = AbstractRpcClient.TOKEN_HANDLERS
.get(tokenKind);
if (tokenSelector != null) {
token = tokenSelector.selectToken(new Text(clusterId), ticket.getTokens());
} else if (LOG.isDebugEnabled()) {
LOG.debug("No token selector found for type " + tokenKind);
}
}
String serverKey = securityInfo.getServerPrincipal();
if (serverKey == null) {
throw new IOException("Can't obtain server Kerberos config key from SecurityInfo");
}
serverPrincipal = SecurityUtil.getServerPrincipal(conf.get(serverKey),
remoteId.address.getAddress().getCanonicalHostName().toLowerCase());
if (LOG.isDebugEnabled()) {
LOG.debug("RPC Server Kerberos principal name for service=" + remoteId.getServiceName()
+ " is " + serverPrincipal);
}
}
this.token = token;
this.serverPrincipal = serverPrincipal;
if (!useSasl) {
authMethod = AuthMethod.SIMPLE;
} else if (token != null) {
authMethod = AuthMethod.DIGEST;
} else {
authMethod = AuthMethod.KERBEROS;
}

// Log if debug AND non-default auth, else if trace enabled.
// No point logging obvious.
if ((LOG.isDebugEnabled() && !authMethod.equals(AuthMethod.SIMPLE)) ||
LOG.isTraceEnabled()) {
// Only log if not default auth.
LOG.debug("Use " + authMethod + " authentication for service " + remoteId.serviceName
+ ", sasl=" + useSasl);
}
reloginMaxBackoff = conf.getInt("hbase.security.relogin.maxbackoff", 5000);
this.remoteId = remoteId;
}

数据批量导入,导致Hbase region 100G,未进行分裂是什么原因

回复

hbasezsh 回复了问题 • 3 人关注 • 1 个回复 • 815 次浏览 • 2018-09-14 10:48 • 来自相关话题

HBase 优化实战

hbase过往记忆 发表了文章 • 0 个评论 • 665 次浏览 • 2018-08-10 12:44 • 来自相关话题

背景

Datastream一直以来在使用HBase分流日志,每天的数据量很大,日均大概在80亿条,10TB的数据。对于像Datastream这种数据量巨大、对写入要求非常高,并且没有复杂查询需求的日志系统来说,选用HBase作为其数据存储平台,无疑是一个非常不错的选择。

HBase是一个相对较复杂的分布式系统,并发写入的性能非常高。然而,分布式系统从结构上来讲,也相对较复杂,模块繁多,各个模块之间也很容易出现一些问题,所以对像HBase这样的大型分布式系统来说,优化系统运行,及时解决系统运行过程中出现的问题也变得至关重要。正所谓:“你”若安好,便是晴天;“你”若有恙,我便没有星期天。

历史现状

HBase交接到我们团队手上时,已经在线上运行有一大段时间了,期间也偶尔听到过系统不稳定的、时常会出现一些问题的言论,但我们认为:一个能被大型互联网公司广泛采用的系统(包括Facebook,twitter,淘宝,小米等),其在性能和可用性上是毋庸置疑的,何况像Facebook这种公司,是在经过严格选型后,放弃了自己开发的Cassandra系统,用HBase取而代之。既然这样,那么,HBase的不稳定、经常出问题一定有些其他的原因,我们所要做的,就是找出这些HBase的不稳定因素,还HBase一个“清白”。“查案”之前,先来简单回顾一下我们接手HBase时的现状(我们运维着好几个HBase集群,这里主要介绍问题最多那个集群的调优):





应用反应经常会过段时间出现数据写入缓慢,导致应用端数据堆积现象,是否可以通过增加机器数量来解决?

  其实,那个时候,我们本身对HBase也不是很熟悉,对HBase的了解,也仅仅在做过一些测试,了解一些性能,对内部结构,实现原理之类的基本上都不怎么清楚。于是刚开始几天,各种问题,每天晚上拉着一男一起摸索,顺利的时候,晚上8,9点就可以暂时搞定线上问题,更多的时候基本要到22点甚至更晚(可能那个时候流量也下去了),通过不断的摸索,慢慢了解HBase在使用上的一些限制,也就能逐渐解决这一系列过程中发现的问题。后面挑几个相对比较重要,效果较为明显的改进点,做下简单介绍。

调优

首先根据目前17台机器,50000+的QPS,并且观察磁盘的I/O利用率和CPU利用率都相当低来判断:当前的请求数量根本没有达到系统的性能瓶颈,不需要新增机器来提高性能。如果不是硬件资源问题,那么性能的瓶颈究竟是什么?

Rowkey设计问题

现象

打开HBase的Web端,发现HBase下面各个RegionServer的请求数量非常不均匀,第一个想到的就是HBase的热点问题,具体到某个具体表上的请求分布如下:




HBase表请求分布

上面是HBase下某张表的region请求分布情况,从中我们明显可以看到,部分region的请求数量为0,而部分的请求数量可以上百万,这是一个典型的热点问题。

原因

HBase出现热点问题的主要原因无非就是rowkey设计的合理性,像上面这种问题,如果rowkey设计得不好,很容易出现,比如:用时间戳生成rowkey,由于时间戳在一段时间内都是连续的,导致在不同的时间段,访问都集中在几个RegionServer上,从而造成热点问题。

解决

知道了问题的原因,对症下药即可,联系应用修改rowkey规则,使rowkey数据随机均匀分布,效果如下:




Rowkey重定义后请求分布

建议

  对于HBase来说,rowkey的范围划定了RegionServer,每一段rowkey区间对应一个RegionServer,我们要保证每段时间内的rowkey访问都是均匀的,所以我们在设计的时候,尽量要以hash或者md5等开头来组织rowkey。

Region重分布

现象

HBase的集群是在不断扩展的,分布式系统的最大好处除了性能外,不停服横向扩展也是其中之一,扩展过程中有一个问题:每次扩展的机器的配置是不一样的,一般,后面新加入的机器性能会比老的机器好,但是后面加入的机器经常被分配很少的region,这样就造成了资源分布不均匀,随之而来的就是性能上的损失,如下:




HBase各个RegionServer请求

上图中我们可以看到,每台RegionServer上的请求极为不均匀,多的好几千,少的只有几十

原因

资源分配不均匀,造成部分机器压力较大,部分机器负载较低,并且部分Region过大过热,导致请求相对较集中。

解决

迁移部分老的RegionServer上的region到新加入的机器上,使每个RegionServer的负载均匀。通过split切分部分较大region,均匀分布热点region到各个RegionServer上。




HBase region请求分布

对比前后两张截图我们可以看到,Region总数量从1336增加到了1426,而增加的这90个region就是通过split切分大的region得到的。而对region重新分布后,整个HBase的性能有了大幅度提高。

建议

Region迁移的时候不能简单开启自动balance,因为balance主要的问题是不会根据表来进行balance,HBase的自动balance只会根据每个RegionServer上的Region数量来进行balance,所以自动balance可能会造成同张表的region会被集中迁移到同一个台RegionServer上,这样就达不到分布式的效果。

基本上,新增RegionServer后的region调整,可以手工进行,尽量使表的Region都平均分配到各个RegionServer上,另外一点,新增的RegionServer机器,配置最好与前面的一致,否则资源无法更好利用。

对于过大,过热的region,可以通过切分的方法生成多个小region后均匀分布(注意:region切分会触发major compact操作,会带来较大的I/O请求,请务必在业务低峰期进行)

HDFS写入超时

现象

HBase写入缓慢,查看HBase日志,经常有慢日志如下:

WARN org.apache.hadoop.ipc.HBaseServer- (responseTooSlow): {"processingtimems":36096, "call":"multi(org.apache.hadoop.hbase.client.MultiAction@7884377e), rpc version=1, client version=29, methodsFingerPrint=1891768260", "client":"xxxx.xxx.xxx.xxxx:44367", "starttimems":1440239670790, "queuetimems":42081, "class":"HRegionServer", "responsesize":0, "method":"multi"}

并且伴有HDFS创建block异常如下:

INFO  org.apache.hadoop.hdfs.DFSClient - Exception in createBlockOutputStream

org.apache.hadoop.hdfs.protocol.HdfsProtoUtil.vintPrefixed(HdfsProtoUtil.java:171)

org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.createBlockOutputStream(DFSOutputStream.java:1105)

org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.nextBlockOutputStream(DFSOutputStream.java:1039)

org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:487)

一般地,HBase客户端的写入到RegionServer下某个region的memstore后就返回,除了网络外,其他都是内存操作,应该不会有长达30多秒的延迟,外加HDFS层抛出的异常,我们怀疑很可能跟底层数据存储有关。

原因

定位到可能是HDFS层出现了问题,那就先从底层开始排查,发现该台机器上10块盘的空间利用率都已经达到100%。按理说,作为一个成熟的分布式文件系统,对于部分数据盘满的情况,应该有其应对措施。的确,HDFS本身可以设置数据盘预留空间,如果部分数据盘的预留空间小于该值时,HDFS会自动把数据写入到另外的空盘上面,那么我们这个又是什么情况?

  最终通过多方面的沟通确认,发现了主要原因:我们这批机器,在上线前SA已经经过处理,每块盘默认预留100G空间,所以当通过df命令查看盘使用率为100%时,其实盘还有100G的预留空间,而HDFS层面我们配置的预留空间是50G,那么问题就来了:HDFS认为盘还有100G空间,并且多于50G的预留,所以数据可以写入本地盘,但是系统层面却禁止了该写入操作,从而导致数据写入异常。

解决

解决的方法可以让SA释放些空间出来便于数据写入。当然,最直接有效的就是把HDFS的预留空间调整至100G以上,我们也正是这样做的,通过调整后,异常不再出现,HBase层面的slow log也没有再出现。同时我们也开启了HDFS层面的balance,使数据自动在各个服务器之间保持平衡。

建议

磁盘满了导致的问题很难预料,HDFS可能会导致部分数据写入异常,MySQL可能会出现直接宕机等等,所以最好的办法就是:不要使盘的利用率达到100%。

网络拓扑

现象

通过rowkey调整,HDFS数据balance等操作后,HBase的确稳定了许多,在很长一段时间都没有出现写入缓慢问题,整体的性能也上涨了很多。但时常会隔一段时间出现些slow log,虽然对整体的性能影响不大,但性能上的抖动还是很明显。

原因

由于该问题不经常出现,对系统的诊断带来不小的麻烦,排查了HBase层和HDFS层,几乎一无所获,因为在大多数情况下,系统的吞吐量都是正常的。通过脚本收集RegionServer所在服务器的系统资源信息,也看不出问题所在,最后怀疑到系统的物理拓扑上,HBase集群的最大特点是数据量巨大,在做一些操作时,很容易把物理机的千兆网卡都吃满,这样如果网络拓扑结构存在问题,HBase的所有机器没有部署在同一个交换机上,上层交换机的进出口流量也有可能存在瓶颈。网络测试还是挺简单的,直接ping就可以,我们得到以下结果:共17台机器,只有其中一台的延迟存在问题,如下:




网络延迟测试:Ping结果

同一个局域网内的机器,延迟达到了毫秒级别,这个延迟是比较致命的,因为分布式存储系统HDFS本身对网络有要求,HDFS默认3副本存在不同的机器上,如果其中某台机器的网络存在问题,这样就会影响到该机器上保存副本的写入,拖慢整个HDFS的写入速度。

解决

  网络问题,联系机房解决,机房的反馈也验证了我们的想法:由于HBase的机器后面进行了扩展,后面加入的机器有一台跟其他机器不在同一个交换机下,而这台机器正是我们找出的有较大ping延时这台,整个HBase物理结构如下:




HBase物理拓扑结构

跟机房协调,调整机器位置,使所有的HBase机器都位于同一个交换机下,问题迎刃而解。

建议

  对于分布式大流量的系统,除了系统本身,物理机的部署和流量规划也相当重要,尽量使集群中所有的机器位于相同的交换机下(有容灾需求的应用除外),集群较大,需要跨交换机部署时,也要充分考虑交换机的出口流量是否够用,网络硬件上的瓶颈诊断起来相对更为困难。

JVM参数调整

解决了网络上面的不稳定因素,HBase的性能又得到进一步的提高,随之也带来了另外的问题。

现象

  根据应用反应,HBase会阶段性出现性能下降,导致应用数据写入缓慢,造成应用端的数据堆积,这又是怎么回事?经过一系列改善后HBase的系统较之以前有了大幅度增长,怎么还会出现数据堆积的问题?为什么会阶段性出现?




从上图看,HBase平均流量QPS基本能达到12w,但是每过一段时间,流量就会下降到接近零点,同时这段时间,应用会反应数据堆积。

原因

  这个问题定位相对还是比较简单,结合HBase的日志,很容易找到问题所在:

org.apache.hadoop.hbase.util.Sleeper - We slept 41662ms instead of 3000ms, this is likely due to a long garbage collecting pause and it's usually bad

通过上述日志,基本上可以判定是HBase的某台RegionServer出现GC问题,导致了服务在很长一段时间内禁止访问。

  HBase通过一系列的调整后,整个系统的吞吐量增加了好几倍,然而JVM的堆大小没有进行相应的调整,整个系统的内存需求变大,而虚拟机又来不及回收,最终导致出现Full GC

解决

   GC问题导致HBase整个系统的请求下降,通过适当调整JVM参数的方式,解决HBase RegionServer的GC问题。

建议

  对于HBase来说,本身不存在单点故障,即使宕掉1,2台RegionServer,也只是使剩下几台的压力有所增加,不会导致整个集群服务能力下降很多。但是,如果其中某台RegionServer出现Full GC问题,那么这台机器上所有的访问都会被挂起,客户端请求一般都是batch发送的,rowkey的随机分布导致部分请求会落到该台RegionServer上,这样该客户端的请求就会被阻塞,导致客户端无法正常写数据到HBase。所以,对于HBase来说,宕机并不可怕,但长时间的Full GC是比较致命的,配置JVM参数的时候,尽量要考虑避免Full GC的出现。

后记

  经过前面一系列的优化,目前Datastream的这套HBase线上环境已经相当稳定,连续运行几个月都没有任何HBase层面由于系统性能不稳定导致的报警,平均性能在各个时间段都比较稳定,没有出现过大幅度的波动或者服务不可用等现象。

本文来自网易实践者社区 查看全部
背景

Datastream一直以来在使用HBase分流日志,每天的数据量很大,日均大概在80亿条,10TB的数据。对于像Datastream这种数据量巨大、对写入要求非常高,并且没有复杂查询需求的日志系统来说,选用HBase作为其数据存储平台,无疑是一个非常不错的选择。

HBase是一个相对较复杂的分布式系统,并发写入的性能非常高。然而,分布式系统从结构上来讲,也相对较复杂,模块繁多,各个模块之间也很容易出现一些问题,所以对像HBase这样的大型分布式系统来说,优化系统运行,及时解决系统运行过程中出现的问题也变得至关重要。正所谓:“你”若安好,便是晴天;“你”若有恙,我便没有星期天。

历史现状

HBase交接到我们团队手上时,已经在线上运行有一大段时间了,期间也偶尔听到过系统不稳定的、时常会出现一些问题的言论,但我们认为:一个能被大型互联网公司广泛采用的系统(包括Facebook,twitter,淘宝,小米等),其在性能和可用性上是毋庸置疑的,何况像Facebook这种公司,是在经过严格选型后,放弃了自己开发的Cassandra系统,用HBase取而代之。既然这样,那么,HBase的不稳定、经常出问题一定有些其他的原因,我们所要做的,就是找出这些HBase的不稳定因素,还HBase一个“清白”。“查案”之前,先来简单回顾一下我们接手HBase时的现状(我们运维着好几个HBase集群,这里主要介绍问题最多那个集群的调优):

menu.saveimg_.savepath20180810124058_.jpg

应用反应经常会过段时间出现数据写入缓慢,导致应用端数据堆积现象,是否可以通过增加机器数量来解决?

  其实,那个时候,我们本身对HBase也不是很熟悉,对HBase的了解,也仅仅在做过一些测试,了解一些性能,对内部结构,实现原理之类的基本上都不怎么清楚。于是刚开始几天,各种问题,每天晚上拉着一男一起摸索,顺利的时候,晚上8,9点就可以暂时搞定线上问题,更多的时候基本要到22点甚至更晚(可能那个时候流量也下去了),通过不断的摸索,慢慢了解HBase在使用上的一些限制,也就能逐渐解决这一系列过程中发现的问题。后面挑几个相对比较重要,效果较为明显的改进点,做下简单介绍。

调优

首先根据目前17台机器,50000+的QPS,并且观察磁盘的I/O利用率和CPU利用率都相当低来判断:当前的请求数量根本没有达到系统的性能瓶颈,不需要新增机器来提高性能。如果不是硬件资源问题,那么性能的瓶颈究竟是什么?

Rowkey设计问题

现象

打开HBase的Web端,发现HBase下面各个RegionServer的请求数量非常不均匀,第一个想到的就是HBase的热点问题,具体到某个具体表上的请求分布如下:
201806281646495b8a5674-3a09-4c12-8f81-5ff43ebc0fa9.jpg

HBase表请求分布

上面是HBase下某张表的region请求分布情况,从中我们明显可以看到,部分region的请求数量为0,而部分的请求数量可以上百万,这是一个典型的热点问题。

原因

HBase出现热点问题的主要原因无非就是rowkey设计的合理性,像上面这种问题,如果rowkey设计得不好,很容易出现,比如:用时间戳生成rowkey,由于时间戳在一段时间内都是连续的,导致在不同的时间段,访问都集中在几个RegionServer上,从而造成热点问题。

解决

知道了问题的原因,对症下药即可,联系应用修改rowkey规则,使rowkey数据随机均匀分布,效果如下:
20180628164659ff3e72b7-9be5-4671-ba17-6a37aafd0cdd.jpg

Rowkey重定义后请求分布

建议

  对于HBase来说,rowkey的范围划定了RegionServer,每一段rowkey区间对应一个RegionServer,我们要保证每段时间内的rowkey访问都是均匀的,所以我们在设计的时候,尽量要以hash或者md5等开头来组织rowkey。

Region重分布

现象

HBase的集群是在不断扩展的,分布式系统的最大好处除了性能外,不停服横向扩展也是其中之一,扩展过程中有一个问题:每次扩展的机器的配置是不一样的,一般,后面新加入的机器性能会比老的机器好,但是后面加入的机器经常被分配很少的region,这样就造成了资源分布不均匀,随之而来的就是性能上的损失,如下:
201806281647298f5c069c-bc91-4706-bb78-ad825e4a15d4.jpg

HBase各个RegionServer请求

上图中我们可以看到,每台RegionServer上的请求极为不均匀,多的好几千,少的只有几十

原因

资源分配不均匀,造成部分机器压力较大,部分机器负载较低,并且部分Region过大过热,导致请求相对较集中。

解决

迁移部分老的RegionServer上的region到新加入的机器上,使每个RegionServer的负载均匀。通过split切分部分较大region,均匀分布热点region到各个RegionServer上。
20180628164752abef950e-8d45-42ca-a5fe-0b1396a96b5f.jpg

HBase region请求分布

对比前后两张截图我们可以看到,Region总数量从1336增加到了1426,而增加的这90个region就是通过split切分大的region得到的。而对region重新分布后,整个HBase的性能有了大幅度提高。

建议

Region迁移的时候不能简单开启自动balance,因为balance主要的问题是不会根据表来进行balance,HBase的自动balance只会根据每个RegionServer上的Region数量来进行balance,所以自动balance可能会造成同张表的region会被集中迁移到同一个台RegionServer上,这样就达不到分布式的效果。

基本上,新增RegionServer后的region调整,可以手工进行,尽量使表的Region都平均分配到各个RegionServer上,另外一点,新增的RegionServer机器,配置最好与前面的一致,否则资源无法更好利用。

对于过大,过热的region,可以通过切分的方法生成多个小region后均匀分布(注意:region切分会触发major compact操作,会带来较大的I/O请求,请务必在业务低峰期进行)

HDFS写入超时

现象

HBase写入缓慢,查看HBase日志,经常有慢日志如下:

WARN org.apache.hadoop.ipc.HBaseServer- (responseTooSlow): {"processingtimems":36096, "call":"multi(org.apache.hadoop.hbase.client.MultiAction@7884377e), rpc version=1, client version=29, methodsFingerPrint=1891768260", "client":"xxxx.xxx.xxx.xxxx:44367", "starttimems":1440239670790, "queuetimems":42081, "class":"HRegionServer", "responsesize":0, "method":"multi"}

并且伴有HDFS创建block异常如下:

INFO  org.apache.hadoop.hdfs.DFSClient - Exception in createBlockOutputStream

org.apache.hadoop.hdfs.protocol.HdfsProtoUtil.vintPrefixed(HdfsProtoUtil.java:171)

org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.createBlockOutputStream(DFSOutputStream.java:1105)

org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.nextBlockOutputStream(DFSOutputStream.java:1039)

org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:487)

一般地,HBase客户端的写入到RegionServer下某个region的memstore后就返回,除了网络外,其他都是内存操作,应该不会有长达30多秒的延迟,外加HDFS层抛出的异常,我们怀疑很可能跟底层数据存储有关。

原因

定位到可能是HDFS层出现了问题,那就先从底层开始排查,发现该台机器上10块盘的空间利用率都已经达到100%。按理说,作为一个成熟的分布式文件系统,对于部分数据盘满的情况,应该有其应对措施。的确,HDFS本身可以设置数据盘预留空间,如果部分数据盘的预留空间小于该值时,HDFS会自动把数据写入到另外的空盘上面,那么我们这个又是什么情况?

  最终通过多方面的沟通确认,发现了主要原因:我们这批机器,在上线前SA已经经过处理,每块盘默认预留100G空间,所以当通过df命令查看盘使用率为100%时,其实盘还有100G的预留空间,而HDFS层面我们配置的预留空间是50G,那么问题就来了:HDFS认为盘还有100G空间,并且多于50G的预留,所以数据可以写入本地盘,但是系统层面却禁止了该写入操作,从而导致数据写入异常。

解决

解决的方法可以让SA释放些空间出来便于数据写入。当然,最直接有效的就是把HDFS的预留空间调整至100G以上,我们也正是这样做的,通过调整后,异常不再出现,HBase层面的slow log也没有再出现。同时我们也开启了HDFS层面的balance,使数据自动在各个服务器之间保持平衡。

建议

磁盘满了导致的问题很难预料,HDFS可能会导致部分数据写入异常,MySQL可能会出现直接宕机等等,所以最好的办法就是:不要使盘的利用率达到100%。

网络拓扑

现象

通过rowkey调整,HDFS数据balance等操作后,HBase的确稳定了许多,在很长一段时间都没有出现写入缓慢问题,整体的性能也上涨了很多。但时常会隔一段时间出现些slow log,虽然对整体的性能影响不大,但性能上的抖动还是很明显。

原因

由于该问题不经常出现,对系统的诊断带来不小的麻烦,排查了HBase层和HDFS层,几乎一无所获,因为在大多数情况下,系统的吞吐量都是正常的。通过脚本收集RegionServer所在服务器的系统资源信息,也看不出问题所在,最后怀疑到系统的物理拓扑上,HBase集群的最大特点是数据量巨大,在做一些操作时,很容易把物理机的千兆网卡都吃满,这样如果网络拓扑结构存在问题,HBase的所有机器没有部署在同一个交换机上,上层交换机的进出口流量也有可能存在瓶颈。网络测试还是挺简单的,直接ping就可以,我们得到以下结果:共17台机器,只有其中一台的延迟存在问题,如下:
20180628164811ef38f3c7-dcc1-492f-83f8-fe7bb8ba94fa.jpg

网络延迟测试:Ping结果

同一个局域网内的机器,延迟达到了毫秒级别,这个延迟是比较致命的,因为分布式存储系统HDFS本身对网络有要求,HDFS默认3副本存在不同的机器上,如果其中某台机器的网络存在问题,这样就会影响到该机器上保存副本的写入,拖慢整个HDFS的写入速度。

解决

  网络问题,联系机房解决,机房的反馈也验证了我们的想法:由于HBase的机器后面进行了扩展,后面加入的机器有一台跟其他机器不在同一个交换机下,而这台机器正是我们找出的有较大ping延时这台,整个HBase物理结构如下:
20180628164824ce396dad-ae37-49b5-8fa7-de67ad187b5c.jpg

HBase物理拓扑结构

跟机房协调,调整机器位置,使所有的HBase机器都位于同一个交换机下,问题迎刃而解。

建议

  对于分布式大流量的系统,除了系统本身,物理机的部署和流量规划也相当重要,尽量使集群中所有的机器位于相同的交换机下(有容灾需求的应用除外),集群较大,需要跨交换机部署时,也要充分考虑交换机的出口流量是否够用,网络硬件上的瓶颈诊断起来相对更为困难。

JVM参数调整

解决了网络上面的不稳定因素,HBase的性能又得到进一步的提高,随之也带来了另外的问题。

现象

  根据应用反应,HBase会阶段性出现性能下降,导致应用数据写入缓慢,造成应用端的数据堆积,这又是怎么回事?经过一系列改善后HBase的系统较之以前有了大幅度增长,怎么还会出现数据堆积的问题?为什么会阶段性出现?
201806281648395c7bae34-f068-41f5-9c8d-a865f5f8f70c.jpg

从上图看,HBase平均流量QPS基本能达到12w,但是每过一段时间,流量就会下降到接近零点,同时这段时间,应用会反应数据堆积。

原因

  这个问题定位相对还是比较简单,结合HBase的日志,很容易找到问题所在:

org.apache.hadoop.hbase.util.Sleeper - We slept 41662ms instead of 3000ms, this is likely due to a long garbage collecting pause and it's usually bad

通过上述日志,基本上可以判定是HBase的某台RegionServer出现GC问题,导致了服务在很长一段时间内禁止访问。

  HBase通过一系列的调整后,整个系统的吞吐量增加了好几倍,然而JVM的堆大小没有进行相应的调整,整个系统的内存需求变大,而虚拟机又来不及回收,最终导致出现Full GC

解决

   GC问题导致HBase整个系统的请求下降,通过适当调整JVM参数的方式,解决HBase RegionServer的GC问题。

建议

  对于HBase来说,本身不存在单点故障,即使宕掉1,2台RegionServer,也只是使剩下几台的压力有所增加,不会导致整个集群服务能力下降很多。但是,如果其中某台RegionServer出现Full GC问题,那么这台机器上所有的访问都会被挂起,客户端请求一般都是batch发送的,rowkey的随机分布导致部分请求会落到该台RegionServer上,这样该客户端的请求就会被阻塞,导致客户端无法正常写数据到HBase。所以,对于HBase来说,宕机并不可怕,但长时间的Full GC是比较致命的,配置JVM参数的时候,尽量要考虑避免Full GC的出现。

后记

  经过前面一系列的优化,目前Datastream的这套HBase线上环境已经相当稳定,连续运行几个月都没有任何HBase层面由于系统性能不稳定导致的报警,平均性能在各个时间段都比较稳定,没有出现过大幅度的波动或者服务不可用等现象。

本文来自网易实践者社区

如何快速统计 HBase 里面有多少行数据

hbasesoso24w 回复了问题 • 9 人关注 • 9 个回复 • 2637 次浏览 • 2018-09-30 16:54 • 来自相关话题

HBase全网最佳学习资料汇总

hbasehbase 发表了文章 • 0 个评论 • 1454 次浏览 • 2018-02-05 21:55 • 来自相关话题

1、前言
 HBase这几年在国内使用的越来越广泛,在一定规模的企业中几乎是必备存储引擎,互联网企业阿里巴巴、京东、小米都有数千台的HBase集群,中国电信的话单、中国人寿的保单都是存储在HBase中。注意大公司有数十个数百个HBase集群,此点跟Hadoop集群很不相同。另外,数据需求,很多公司是mysql+hbase+hadoop(spark),满足关系型数据库需求,满足大规模结构化存储需求,满足复杂分析的需求。如此流行的原因来源于很多方面,如:
  - 开源繁荣的生态:1. 任何公司倒闭了,开源的HBase还在 2.几乎每家公司都可以去下载源码,改进她,再反馈给社区,就如阿里已经反馈了数百个patch了。加入的人越多,引擎就越好
  - 跟HADOOP深度结合:本就同根同源,在数据存储在HBase后,如果想复杂分析,则非常方便
  - 高扩展、高容量、高性能、低成本、低延迟、稀疏宽表、动态列、TTL、多版本等最为关键,起源google论文,发扬社区及广大互联网公司,设计之初就是为存储互联网,后经过多年的改进升级,如今已经是结构化存储的事实标准

以下资料会一直更新中......请大家关注!

2、书籍
最好买纸质书籍,集中时间看下
HBase权威指南(HBase: The Definitive Guide):理论多一些HBase实战:实践多一些
3、总结性
HBase2.0: HBase2.0 :预计今年会发布,hbase2.0是革命性的版本HBase Phoenix:Apache Phoenix与HBase:HBase之上SQL的过去,现在和未来 社区hbase博客:https://blogs.apache.org/hbase/
4、方法论
学术界关于HBase应用场景(物联网/车联网/交通/电力等)研究大全: HBase在互联网领域有广泛的应用,比如:互联网的消息系统的存储、订单的存储、搜索原材料的存储、用户画像数据的存储等。得益于HBase海量的存储量及超高并发写入读取量。HBase在09年就开始在工业界大范围使用,在学术界,也有非常多的高校、机构在研究HBase应用于不同的行业,本文主要梳理下这些资料(主要是中文资料,有一些是硕士论文\期刊),这些很多都在工业界使用了。HBase使用场景和成功案例  存储互联网的初心不变 一种基于物联网大数据的设备信息采集系统及方法:怎么使用HBase、sparkStreaming、redis处理物联网大数据一种基于HBase的智能电网时序大数据处理方方案:一种基于HBase的智能电网时序大数据处理方方案HBase配合GeoHash算法支持经纬度:此文主要讲GeoHash算法的基于HBase的海量GIS数据分布式处理实践:设计了一种基于分布式数据库HBase的GIS数据管理系统。系统优化了栅格数据的生成和存储过程,将海量栅格数据直接写入HBase存储、索引。同时,针对矢量空间数据的存储、索引与检索,提出了一种新的rowkey设计,既考虑经纬度,又考虑空间数据类型和属性,使得在按空间位置检索矢量地理信息时,能通过HBase的rowkey迅速定位需要返回的数据。在HBase的集群环境上用真实GIS数据对上述方法进行了验证,结果表明,提出的系统具有较高的海量数据存储和检索性能,实现了海量地理信息数据的高效存储和实时高速检索。基于HBase的金融时序数据存储系统:金融类时序数据的存储方案,写的还是结合实际场景的。
5、各大公司的实践
基本围绕在用户画像、安全风控、订单存储、交通轨迹、物理网、监控、大数据中间存储、搜索、推荐等方面:
阿里巴巴-大数据时代的结构化存储HBase在阿里的应用实践:讲述在阿里巴巴集团的实践,HBase在阿里集团已经10000台左右,主要在订单、监控、风控、消息、大数据计算等领域使用阿里巴巴搜索-Hbase在阿里巴巴搜索中的完美应用实践:讲述在搜索场景下hbase的应用及相关的改进日均采集1200亿数据点,腾讯千亿级服务器监控数据存储实践:本文将从当前存储架构存在的问题出发,介绍从尝试使用 Opentsdb 到自行设计 Hbase 存储方案来存储 TMP 服务器海量监控数据的实践历程。滴滴-HBase在滴滴出行的应用场景和最佳实践:统计结果、报表类数据、原始事实类数据、中间结果数据、线上系统的备份数据的一些应用HBase上搭建广告实时数据处理平台]:主要分享 1. 如何通过HBase实现数据流实时关联 2. 如何保证重要的计费数据不重不丢 3. HBase实战经验,优化负载均衡、读写缓存、批量读写等性能问题HBase在京东的实践 :跟阿里一样,京东各个业务线使用了HBase,如:风控、订单、商品评价等中国人寿基于HBase的企业级大数据平台:使用一个大跨表存储所有的保单,HBase宽表的实践HBase在Hulu的使用和实践:用户画像、订单存储系统、日志存储系统的使用Apache HBase at Netease:在报表、监控、日志类业务、消息类业务、推荐类业务、风控类业务有所使用,另外讲述了一些优化的点。10 Million Smart Meter Data with Apache HBase:讲述Hitachi为什么选择hbase及在HBase方面的应用G7:如何用云计算链接30万车辆--EMR&Hbase 在物联网领域的实践及解决方案 讲述了怎么使用spark及hbase来满足物联网的需求
6、结尾
  这些资料是笔者整理,以供有大规模结构化需求的用户及HBase爱好者学习交流,以使用HBase更好的解决实际的问题。欢迎传播,原文路径:http://www.hbase.group/hbase/?/article/1

7、声明
以上资料来自互联网,如果侵权,请联系我删除 查看全部
1、前言
 HBase这几年在国内使用的越来越广泛,在一定规模的企业中几乎是必备存储引擎,互联网企业阿里巴巴、京东、小米都有数千台的HBase集群,中国电信的话单、中国人寿的保单都是存储在HBase中。注意大公司有数十个数百个HBase集群,此点跟Hadoop集群很不相同。另外,数据需求,很多公司是mysql+hbase+hadoop(spark),满足关系型数据库需求,满足大规模结构化存储需求,满足复杂分析的需求。如此流行的原因来源于很多方面,如:
  - 开源繁荣的生态:1. 任何公司倒闭了,开源的HBase还在 2.几乎每家公司都可以去下载源码,改进她,再反馈给社区,就如阿里已经反馈了数百个patch了。加入的人越多,引擎就越好
  - 跟HADOOP深度结合:本就同根同源,在数据存储在HBase后,如果想复杂分析,则非常方便
  - 高扩展、高容量、高性能、低成本、低延迟、稀疏宽表、动态列、TTL、多版本等最为关键,起源google论文,发扬社区及广大互联网公司,设计之初就是为存储互联网,后经过多年的改进升级,如今已经是结构化存储的事实标准

以下资料会一直更新中......请大家关注!

2、书籍
最好买纸质书籍,集中时间看下
  • HBase权威指南(HBase: The Definitive Guide):理论多一些
  • HBase实战:实践多一些

3、总结性

4、方法论

5、各大公司的实践
基本围绕在用户画像、安全风控、订单存储、交通轨迹、物理网、监控、大数据中间存储、搜索、推荐等方面:

6、结尾
  这些资料是笔者整理,以供有大规模结构化需求的用户及HBase爱好者学习交流,以使用HBase更好的解决实际的问题。欢迎传播,原文路径:http://www.hbase.group/hbase/?/article/1

7、声明
以上资料来自互联网,如果侵权,请联系我删除

hbase2什么时候发布?

hbasehmaster 回复了问题 • 5 人关注 • 3 个回复 • 2849 次浏览 • 2018-03-12 14:37 • 来自相关话题

基于Solr创建HBase二级索引,数据一致性问题有什么解决方法吗

回复

Solrhadoop 回复了问题 • 2 人关注 • 1 个回复 • 245 次浏览 • 2019-09-04 10:29 • 来自相关话题

java.io.IOException: Added a key not lexically larger

回复

hbasekite 发起了问题 • 1 人关注 • 0 个回复 • 112 次浏览 • 2019-07-18 11:27 • 来自相关话题

phoenix使用hbase命令激活异步建立的二级索引

回复

Phoenixxbyang18 回复了问题 • 5 人关注 • 6 个回复 • 2400 次浏览 • 2019-07-17 09:08 • 来自相关话题

加入群一年多转发你们自己群主发的东西踢我?

回复

hbasejiatianyao 发起了问题 • 1 人关注 • 0 个回复 • 196 次浏览 • 2019-07-13 08:31 • 来自相关话题

waiting for 51 actions to finish on table: xxxx

回复

hbasecodermonkey 回复了问题 • 4 人关注 • 2 个回复 • 1718 次浏览 • 2019-07-11 19:56 • 来自相关话题

pom引入hbase2.0.2依赖问题

回复

hbasechb 发起了问题 • 1 人关注 • 0 个回复 • 201 次浏览 • 2019-06-12 22:38 • 来自相关话题

spark连接Phoenix报错

回复

Phoenixyangchenggang 回复了问题 • 4 人关注 • 7 个回复 • 1878 次浏览 • 2019-05-11 10:57 • 来自相关话题

hbase执行手动大合并的时候,执行了一周还是没有结束,是怎么回事?

回复

hbase果农 发起了问题 • 1 人关注 • 0 个回复 • 252 次浏览 • 2019-04-22 08:50 • 来自相关话题

hbase 奇怪的metric错误

回复

hbasegp1314 回复了问题 • 3 人关注 • 2 个回复 • 843 次浏览 • 2019-04-01 13:09 • 来自相关话题

有关导出数据问题

回复

hbase过往记忆 回复了问题 • 3 人关注 • 3 个回复 • 311 次浏览 • 2019-03-21 18:19 • 来自相关话题

hbase regionserver启动不了

回复

hbasezb 回复了问题 • 3 人关注 • 3 个回复 • 312 次浏览 • 2019-03-21 14:11 • 来自相关话题

Flink + HBase 如何实现实时统计pv uv

回复

hbasemachuan 回复了问题 • 2 人关注 • 1 个回复 • 1057 次浏览 • 2019-03-10 22:41 • 来自相关话题

hbase coprocessor preput方法没有触发

回复

hbasewukaishanda08 回复了问题 • 2 人关注 • 2 个回复 • 358 次浏览 • 2019-02-28 18:24 • 来自相关话题

hbase shell的问题

回复

hbasezxpns18 回复了问题 • 3 人关注 • 2 个回复 • 399 次浏览 • 2019-02-28 15:32 • 来自相关话题

cdh4.5.0 hbase 如何定时删除数据

回复

hbasezxpns18 回复了问题 • 3 人关注 • 2 个回复 • 388 次浏览 • 2019-02-28 15:24 • 来自相关话题

请问如何发表技术文章

回复

hbase过往记忆 回复了问题 • 2 人关注 • 1 个回复 • 253 次浏览 • 2019-02-28 10:36 • 来自相关话题

hbase中存储视频文件,下载视频文件流程?

回复

hbasezxpns18 回复了问题 • 6 人关注 • 6 个回复 • 1043 次浏览 • 2019-02-28 09:53 • 来自相关话题

HBase coprocessor介绍及使用

hbase过往记忆 发表了文章 • 0 个评论 • 455 次浏览 • 2019-03-28 10:52 • 来自相关话题

讲师:陈杨——快手大数据高级研发工程师
毕业于浙江大学,现负责快手HBase的维护与研发,支持视频、特征、用户画像、IM等海量数据的存储;
一直致力于大数据基础架构和hadoop生态的学习与研发;同时在hbase与hdfs的基础上,和团队一起研发了大数据存储与分析相关系统,如blobstore、bitbase等。

内容概要:

(1)讲解hbase coprocessor的原理以及使用场景,
(2) coprocessor整个流程实战,包括开发,加载,运行以及管理
(3)结合1,2分析coprocessor在rsgroup中的具体使用 查看全部
讲师:陈杨——快手大数据高级研发工程师
毕业于浙江大学,现负责快手HBase的维护与研发,支持视频、特征、用户画像、IM等海量数据的存储;
一直致力于大数据基础架构和hadoop生态的学习与研发;同时在hbase与hdfs的基础上,和团队一起研发了大数据存储与分析相关系统,如blobstore、bitbase等。

内容概要:

(1)讲解hbase coprocessor的原理以及使用场景,
(2) coprocessor整个流程实战,包括开发,加载,运行以及管理
(3)结合1,2分析coprocessor在rsgroup中的具体使用

hbase优化之旅(四)-regionserver从17优化到10台的小结

hbasechuxiao 发表了文章 • 3 个评论 • 623 次浏览 • 2019-03-25 18:47 • 来自相关话题

本文是前几篇文章的小结,介绍如何通过技术手段,挖掘hbase服务的潜力,将分组的服务器数从17台降低到10台。
首发于专栏 :https://zhuanlan.zhihu.com/p/60357239 

目录
确定优化目标
系统资源瓶颈和优化效果
cpu利用率
内存使用
网络IO
磁盘IO
存储量
稳定性
综述
优化细节
合并region
hbase参数调整
未来的工作
近期工作
中远期工作

确定优化目标
沟通交流后,业务方更看重降低成本。数据量梳理后略有降低,保证吞吐,无长期请求堆积前提下可以放宽延时要求。为了更快的进行优化,放宽稳定性可以要求接受短期波动。
另外,该分组的regionserver之前存在不稳定的问题,这次优化也一并解决。

系统资源瓶颈和优化效果
降低成本,即省机器,就是用更少的服务器满足业务需求。需要搞清楚单台服务器的瓶颈在哪。下面分析单台服务器的指标和优化效果。
cpu利用率
因为hbase是重IO,轻计算的服务,一般来说hbase服务cpu利用率较低。优化前单台cpu利用率平均在5%,峰值在10%左右。直接机器减半也没有压力。
空闲的cpu利用率,可以用来置换更高的存储压缩比,节约存储空间。更多的业务线程和gc线程,提高吞吐能力,减少gc对系统吞吐和延时毛刺的影响。
峰值cpu在40%以下,说明cpu资源充足富裕,可以减机器,用cpu换存储空间,提高业务吞吐。


内存使用
256GB内存,datanode 4g,regionserver堆内50g,堆外100g,空闲大概100g。可以提高单机regionserver内存到堆内100g,堆外120g,留10%的空闲。
提高内存的好处很多。单机负载不变的前提下,增加堆内内存,可以降低gc频率减少毛刺出现频率,降低gc时间占比提高吞吐(另外如果mixed gc周期能超过大多数region flush的周期,回收会更有效率)。增加memstore写缓存,可以降低写放大效应,从而降低网络和磁盘IO。降低写放大,还可以减少compact,提高缓存有效性从而增加缓存命中率,进一步降低读IO,减少读请求延时。增加堆外内存,可以缓存命中率。
举个例子,一台服务器提高内存使用量,同时region数变为其他服务器的2倍。堆外内存增加20%而region数翻倍,单纯数字上来看缓存命令率应该下降,实际稳定后命中率保持在96%高于其他服务器的94%,这就是降低compact提高命中率的收益。
增大内存的坏处是,单次gc时间会更长,极端情况下请求延时的毛刺会更明显。另外一些hbase内部的数据结构,如memstore的ConcurrentSkipListMap,对象数太多性能会下降的很厉害,需要结合用户需求,在region数,hbase-site参数,gc参数上综合考虑,做一个权衡。


网络IO
网络In 20MB,Out 40到100MB波动。万兆网卡峰值也就百MB以内,机器数砍半没压力。

磁盘IO
非major compact时段,磁盘读流量较高,高峰期200MB到300MB,主要来自compact。写流量50MB/s。磁盘流量较高是个瓶颈,需要技术优化。
磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5左右),如果不考虑服务端filter过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
用户的业务场景没用filter,所以可以推论读IO主要来自compact ,验证了写放大效应明显。
经过参数优化降低写放大效应,region数不变,单机磁盘读IO下降到之前的1/3,80MB左右。
又经过region合并,参数继续优化,单机两倍region负载,读Io保持在150MB到200MB之间,比之前单倍region负载还要低很多。

存储量
集群存储峰值大概有三个时间点,每周major compact前,每周major compact后,major compact中(大概是major compact前存储*(1+并发major region数/总region数))。
目前没有明确用哪个存储量衡量峰值,一般定期检查,发现用到差不多了就和用户沟通加机器。不允许独立分组存储总量大于分组机器所提供的存储总量。
在major compact前取了数据的总量,刚好跟10台的存储量差不多,可以降低到10台。

稳定性
如系列文章上一篇gc执行细节和参数调优方法论详所说,分组之前存在稳定性的问题。出现问题时需要人工操作迁移region,重启。当机器数减到17台,直接减机器立刻加剧了问题出现的频率。经分析,问题主要出在gc参数上,详见上一篇文章。

综述
通过瓶颈分析,解决了磁盘IO问题后,10台服务器是存储量瓶颈,所以第一步的目标是降到10台。



优化细节
合并region
之前分组17台,单机region数大概500上下,region数太多导致flush都是小文件,写放大罪魁祸首。合并小于10g 的region,将region数降低到单机130+,写放大效应立刻降低了,可以减少机器数了。
一个批量写请求,regionserver内写多个region是串行的,这个角度减少region数可以改善写延时。同时单个region memstore太大,ConcurrentSkipListMap的结构插入性能会降低。目前没发现减少region后明显的写延时差别。杭州的同事分享过阿里对memstore数据结构的优化,版本升级到1.4.8后可以评估能否用的到。
另外,大region会导致major compact压力更大。这个可以通过修改compact policy来解决。

hbase参数调整
增大内存
堆内存 50g->100g
堆外内存 100g->120g
大内存降低写放大效应,提高读缓存命中率,支持更高的吞吐能力。改善了gc表现。


基础配置
hbase.regionserver.handler.count 192->384
hbase.ipc.server.callqueue.read.share 无->0.4

hbase.ipc.server.callqueue.handler.factor 无->0.2
两倍region后,监控看当前handler经常达到192上限,按其他公司经验调大一倍。有时能打到上限。
线程多可以减少队列等待时间,可能增加请求处理时间,监控看,调大能显著减少队列等待时间p99,请求时间p99没有明显变化。
读写分离主要是能降低p99,避免慢写入/高并发scan等耗时长的操作堵塞句柄。192个句柄时,等待队列的p99有时会持续在500ms-1s左右。调大到384,增加读写分离,等待队列p99就维持在100ms以下了。
5个句柄使用一个队列,是为了降低高并发时的锁资源争抢。需要权衡队列比例,如果1,2个句柄就使用一个队列,很容易几个慢请求就把队列里的后续请求都堵住了。目前设置为5:1.

memstrore
hbase.hstore.flusher.count 15-->8 flush线程数
base.regionserver.optionalcacheflushinterval 无->7200000 (1小时到2小时)
hbase.regionserver.hlog.blocksize 无 -> 268435456 (实际值没变,由使用hdfs块大小变为显式设置)
hbase.regionserver.maxlogs 52 -> 200

除了regionserver重启时memstore flush外,有几种情况会触发memstore flush,分别是单个region的memstore达到上限,单个region的memstore距离上次flush过了刷新周期,hlogs达到上限flush涉及的还未flush的region,总memstore达到内存设置上限。这几种情况按照从好到差顺序排列,越靠后对系统的稳定性影响越高,应尽量避免。

堆内存的增加,直接增大了memstore内存上限,增大了单region flush的容量,可以刷大文件减少写放大效应。同时尽量让region写满128MB再flush,可以错开flush时间,错开compact时间,降低磁盘IO峰值,减少flush和compact排队现象。
10台均分region,调整后单台的memstore在10g到30g波动。为了尽量128MB在刷,其他几种flush方式的参数要跟着调整。

虽然我们要避免后面几种flush情况出现,但当业务突然有写入热点,或机器重启各region的memstore重置,可能会触发定时刷新/达到hlogs上限引起集中flush。为降低同时flush并发高引起的问题,降低了并发flush数。由于memstore足够大,单次flush周期长,即使控制flush并发,也不会充暴memstore内存造成写堵塞。
此外,控制flush数可以间接控制minor compact的压力。

按10台regionserver计算规模和请求量,白天绝大多数region在2个内memstore达到128MB,2小时刷新可以确保绝大多数region写满128MB自动刷新,减少写放大。如果按默认1小时,重启后的前2,3天里,会有很多region在相同时间flush,触发compact,系统压力很大。2,3天后,靠着刷新时间每次的随机波动才能慢慢分散开。所以应确保白天峰值写入量时,大多数region都能在刷新周期里写满flush。

hlogs过少会造成刷新周期太短。以之前经常延时变长的一台服务器为例,平均5到10分钟强刷一次,刷的都是小文件,带来了严重的读写放大后果。之前region数三倍于现在,和gc参数不合适一起造成了偶发的gc时间占比高影响业务的问题。 另外,目前hlogs 50的配置会造成同时flush大量region,同时compact,系统压力大,造成请求和吞吐的毛刺。
maxlogs的配置多大合适?maxlogs 从90,到120,150,200,250,300,350,400都实验过,越大的hlogs对缓解region写入不均,调大flush尺寸和周期越有利 。
单纯按照流传的公式来看, 下限 50 * 1024/(256*0.95)= 210,上限60 * 1024/(256*0.95)= 252,应该在210到252之间。实际由于各region会陆续触发flush,hlogs即使到达252,memstore总内存可能只有10-20g,依然可以调大。
hlogs上限提高能解决重启后同时flush密集的问题。重启后,各region memstore都是从0开始缓存,hlogs到上限时如果大量region没写满,会触发大量region同时 flush,这些region的memstore再次清零了,下个周期依然有大量region同时flush,打散速度慢于刷新周期触发的flush。hlogs上限出发的同时flush,compact对gc压力很大,请求延时会周期性显著提升。如果256MB flush,8台服务器,hlogs需要到400。128MB则200即可。
hlogs过多可能有什么影响?1.重启时间变长。重试时间长本质是总memstore增大,flush时间长造成的。之前17台规模单台重启大概1分钟,8台规模单台大概1分50秒,总时间并没有增加。再就是如果某台regionserver挂掉,集群要重读未flush的hlogs,hlogs多了会增加重读的量,增加集群负担。

综上所述,最终flush size定为128MB,hlogs上限定为200。

读cache
hfile.block.cache.size 0.19 ->0.2
hbase.bucketcache.size 102400 -> 132000
hbase.bucketcache.percentage.in.combinedcache 0.9 -> 0.85

缓存大小配合堆内堆外内存增加的调整,缓存变成堆外上限112g,堆内20g。L1和L2的比例按实际线上情况由0.9调整为0.85.


compact
hbase.regionserver.thread.compaction.small 6 -> 8
hbase.regionserver.thread.compaction.large 3 -> 4

略微提高minor compact 和major compact速度,尤其是major compact速度,以便机器减少到一半时,夜里能major完。调到12,gc压力过大,所以只是微调。

hdfs
dfs.client.hedged.read.threadpool.size 50 ->300 
dfs.client.hedged.read.threshold.millis 150->500
hbase.regionserver.hlog.slowsync.ms 无->400
compact负载一高,200线程池会报大量的线程池满,资源不够用,所以调到300。
我们用多路读是为了当磁盘故障时,可以读其他副本。如果超时时间太低,可以读本地的去读了远程副本,显著增大集群网络和磁盘IO。读包括compact的读,是轻延时重吞吐的,集群磁盘IO负载高,延时增加,触发多路读又增大了集群的IO压力。尤其是本地化不是100%时,会读其他机器上的副本,400毫秒也依然容易超时,所以超时时间改为500,确保在一般的高负载和非本地化场景中,也不会给集群额外的压力,只有磁盘真正故障堵塞读的时候再读其他副本上。由于95%以上的读都来自内存缓存,500毫秒的最大超时时间并不会造成显著的读请求延时升高,毕竟常态的gc也要几百ms时间。
负载稍微高点,日志文件满屏都是wal log slow,淹没了其他需要关注的问题。由于wal是单线程顺序写单文件,写入速度是有瓶颈的,调到400,只有负载较高时才会打印。

gc参数调整
请看上一篇,本文不再叙述。

未来的工作
近期工作
打散major compact执行时间
现在集群内所有业务分组同一天晚上进行major compact ,集群网络和磁盘IO压力大,存储量也会同时飙升。major compact即将打散到每周7天的晚上,以降低集群的压力。

换compact policy
compact policy优化,需要用户配合做客户端代码优化,和业务方暂定4月初共建。
现在所有业务用的都是默认的default policy,中规中矩。而该项目业务场景非常适合用Date Tiered Compaction,该策略能极大降低compact写放大效应尤其是major compact的压力,并且提升近期热点数据查询速度。
更换该策略,需要业务方略微修改客户端代码,读写时增加TTL的设置,否则可能会降低查询速度。

换压缩算法
换高压缩比算法gz能进一步减少存储总量,已有经验是可以降低30%左右。即存储量视角,可以降到7台服务器规模。换压缩算法会增加cpu利用率,可能对用户读写造成未知的影响,待4月和用户共建。


中远期工作
2副本
好处是存储量立刻降低1/3,坏处是集群同时坏2块盘的概率比坏3块高得多,更容易丢数据。一般是偏离线,稳定性要求不高,存储量偏高的业务,数据在hive有一份,即使有数据缺失可以很快从hive导一份。需要和用户进一步确定业务模式是否能采用。

超卖
目前成本分摊策略,独立业务分组按regionserver服务器数分摊成本,如果独立业务分组存储总量大于分组机器所提供的存储总量,即认为存储量达到瓶颈。就必须要增加机器,不同的业务瓶颈不一样,按存储量严格限制,不利于提升集群资源的整体利用率。
所以从整个集群的资源利用率来看,允许部分吞吐,延时要求不高的业务使用更多的存储,即存储超卖,可以更有效提高集群资源利用率,释放数据引擎潜力,降低业务方使用成本,为公司省钱。
超卖涉及三个维度。超卖存储每TB价格,超卖存储量如何计算。超卖比例,
现在每TB成本是整机打包折算的,而超卖的存储只涉及纯磁盘的折算成本。要超卖存储,需要额外提供超卖存储的每TB价格,这个价格应该比整机的每TB成本低一些。
超卖存储量可以用定时程序收集分组的hdfs存储量,保留最高值,除0.9作为该月实际存储用量,减去分组机器数提供的存储就是超卖量。
超卖比例,前期可以先允许超卖分组存储的20%,并监控集群整体磁盘利用率情况。超卖试运行一段时间后,如果集群存储依然空闲,可以尝试提高超卖比例上限。
目前成本分摊的方式,只支持业务分组按整机打包折算,没有超卖的分摊方式。待评估实际需求后,再来决定是否允许超卖。
如果有业务方需要过高的超卖比例,不适用于现在的集群架构,需要考虑ssd和sata混部的架构。

ssd和sata混部
目前业务按数据量和期望延时的不同分了两个集群,1个是ssd集群,一个是sata盘集群。ssd读写更快,但成本高,存储量有限。
业内最顶尖的做法,是允许ssd和sata盘混部,好处有2。一个是3副本一个是ssd2个是sata盘,保证性能的同时降低存储成本。另一个是时间相关数据按时间分块存储在不同介质上,例如支付宝账单,3个月内ssd,1年内sata盘,一年前的历史数据就存储在更便宜的存储介质上了。历史类数据都适合用这种方式。
混部的方式,在软件,集群架构,机器采购方面都有颇多挑战,是个长期的工作。 查看全部
本文是前几篇文章的小结,介绍如何通过技术手段,挖掘hbase服务的潜力,将分组的服务器数从17台降低到10台。
首发于专栏 :https://zhuanlan.zhihu.com/p/60357239 

目录
确定优化目标
系统资源瓶颈和优化效果
cpu利用率
内存使用
网络IO
磁盘IO
存储量
稳定性
综述
优化细节
合并region
hbase参数调整
未来的工作
近期工作
中远期工作

确定优化目标
沟通交流后,业务方更看重降低成本。数据量梳理后略有降低,保证吞吐,无长期请求堆积前提下可以放宽延时要求。为了更快的进行优化,放宽稳定性可以要求接受短期波动。
另外,该分组的regionserver之前存在不稳定的问题,这次优化也一并解决。

系统资源瓶颈和优化效果
降低成本,即省机器,就是用更少的服务器满足业务需求。需要搞清楚单台服务器的瓶颈在哪。下面分析单台服务器的指标和优化效果。
cpu利用率
因为hbase是重IO,轻计算的服务,一般来说hbase服务cpu利用率较低。优化前单台cpu利用率平均在5%,峰值在10%左右。直接机器减半也没有压力。
空闲的cpu利用率,可以用来置换更高的存储压缩比,节约存储空间。更多的业务线程和gc线程,提高吞吐能力,减少gc对系统吞吐和延时毛刺的影响。
峰值cpu在40%以下,说明cpu资源充足富裕,可以减机器,用cpu换存储空间,提高业务吞吐。


内存使用
256GB内存,datanode 4g,regionserver堆内50g,堆外100g,空闲大概100g。可以提高单机regionserver内存到堆内100g,堆外120g,留10%的空闲。
提高内存的好处很多。单机负载不变的前提下,增加堆内内存,可以降低gc频率减少毛刺出现频率,降低gc时间占比提高吞吐(另外如果mixed gc周期能超过大多数region flush的周期,回收会更有效率)。增加memstore写缓存,可以降低写放大效应,从而降低网络和磁盘IO。降低写放大,还可以减少compact,提高缓存有效性从而增加缓存命中率,进一步降低读IO,减少读请求延时。增加堆外内存,可以缓存命中率。
举个例子,一台服务器提高内存使用量,同时region数变为其他服务器的2倍。堆外内存增加20%而region数翻倍,单纯数字上来看缓存命令率应该下降,实际稳定后命中率保持在96%高于其他服务器的94%,这就是降低compact提高命中率的收益。
增大内存的坏处是,单次gc时间会更长,极端情况下请求延时的毛刺会更明显。另外一些hbase内部的数据结构,如memstore的ConcurrentSkipListMap,对象数太多性能会下降的很厉害,需要结合用户需求,在region数,hbase-site参数,gc参数上综合考虑,做一个权衡。


网络IO
网络In 20MB,Out 40到100MB波动。万兆网卡峰值也就百MB以内,机器数砍半没压力。

磁盘IO
非major compact时段,磁盘读流量较高,高峰期200MB到300MB,主要来自compact。写流量50MB/s。磁盘流量较高是个瓶颈,需要技术优化。
磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5左右),如果不考虑服务端filter过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
用户的业务场景没用filter,所以可以推论读IO主要来自compact ,验证了写放大效应明显。
经过参数优化降低写放大效应,region数不变,单机磁盘读IO下降到之前的1/3,80MB左右。
又经过region合并,参数继续优化,单机两倍region负载,读Io保持在150MB到200MB之间,比之前单倍region负载还要低很多。

存储量
集群存储峰值大概有三个时间点,每周major compact前,每周major compact后,major compact中(大概是major compact前存储*(1+并发major region数/总region数))。
目前没有明确用哪个存储量衡量峰值,一般定期检查,发现用到差不多了就和用户沟通加机器。不允许独立分组存储总量大于分组机器所提供的存储总量。
在major compact前取了数据的总量,刚好跟10台的存储量差不多,可以降低到10台。

稳定性
如系列文章上一篇gc执行细节和参数调优方法论详所说,分组之前存在稳定性的问题。出现问题时需要人工操作迁移region,重启。当机器数减到17台,直接减机器立刻加剧了问题出现的频率。经分析,问题主要出在gc参数上,详见上一篇文章。

综述
通过瓶颈分析,解决了磁盘IO问题后,10台服务器是存储量瓶颈,所以第一步的目标是降到10台。



优化细节
合并region

之前分组17台,单机region数大概500上下,region数太多导致flush都是小文件,写放大罪魁祸首。合并小于10g 的region,将region数降低到单机130+,写放大效应立刻降低了,可以减少机器数了。
一个批量写请求,regionserver内写多个region是串行的,这个角度减少region数可以改善写延时。同时单个region memstore太大,ConcurrentSkipListMap的结构插入性能会降低。目前没发现减少region后明显的写延时差别。杭州的同事分享过阿里对memstore数据结构的优化,版本升级到1.4.8后可以评估能否用的到。
另外,大region会导致major compact压力更大。这个可以通过修改compact policy来解决。

hbase参数调整
增大内存

堆内存 50g->100g
堆外内存 100g->120g
大内存降低写放大效应,提高读缓存命中率,支持更高的吞吐能力。改善了gc表现。


基础配置
hbase.regionserver.handler.count 192->384
hbase.ipc.server.callqueue.read.share 无->0.4

hbase.ipc.server.callqueue.handler.factor 无->0.2
两倍region后,监控看当前handler经常达到192上限,按其他公司经验调大一倍。有时能打到上限。
线程多可以减少队列等待时间,可能增加请求处理时间,监控看,调大能显著减少队列等待时间p99,请求时间p99没有明显变化。
读写分离主要是能降低p99,避免慢写入/高并发scan等耗时长的操作堵塞句柄。192个句柄时,等待队列的p99有时会持续在500ms-1s左右。调大到384,增加读写分离,等待队列p99就维持在100ms以下了。
5个句柄使用一个队列,是为了降低高并发时的锁资源争抢。需要权衡队列比例,如果1,2个句柄就使用一个队列,很容易几个慢请求就把队列里的后续请求都堵住了。目前设置为5:1.

memstrore
hbase.hstore.flusher.count 15-->8 flush线程数
base.regionserver.optionalcacheflushinterval 无->7200000 (1小时到2小时)
hbase.regionserver.hlog.blocksize 无 -> 268435456 (实际值没变,由使用hdfs块大小变为显式设置)
hbase.regionserver.maxlogs 52 -> 200

除了regionserver重启时memstore flush外,有几种情况会触发memstore flush,分别是单个region的memstore达到上限,单个region的memstore距离上次flush过了刷新周期,hlogs达到上限flush涉及的还未flush的region,总memstore达到内存设置上限。这几种情况按照从好到差顺序排列,越靠后对系统的稳定性影响越高,应尽量避免。

堆内存的增加,直接增大了memstore内存上限,增大了单region flush的容量,可以刷大文件减少写放大效应。同时尽量让region写满128MB再flush,可以错开flush时间,错开compact时间,降低磁盘IO峰值,减少flush和compact排队现象。
10台均分region,调整后单台的memstore在10g到30g波动。为了尽量128MB在刷,其他几种flush方式的参数要跟着调整。

虽然我们要避免后面几种flush情况出现,但当业务突然有写入热点,或机器重启各region的memstore重置,可能会触发定时刷新/达到hlogs上限引起集中flush。为降低同时flush并发高引起的问题,降低了并发flush数。由于memstore足够大,单次flush周期长,即使控制flush并发,也不会充暴memstore内存造成写堵塞。
此外,控制flush数可以间接控制minor compact的压力。

按10台regionserver计算规模和请求量,白天绝大多数region在2个内memstore达到128MB,2小时刷新可以确保绝大多数region写满128MB自动刷新,减少写放大。如果按默认1小时,重启后的前2,3天里,会有很多region在相同时间flush,触发compact,系统压力很大。2,3天后,靠着刷新时间每次的随机波动才能慢慢分散开。所以应确保白天峰值写入量时,大多数region都能在刷新周期里写满flush。

hlogs过少会造成刷新周期太短。以之前经常延时变长的一台服务器为例,平均5到10分钟强刷一次,刷的都是小文件,带来了严重的读写放大后果。之前region数三倍于现在,和gc参数不合适一起造成了偶发的gc时间占比高影响业务的问题。 另外,目前hlogs 50的配置会造成同时flush大量region,同时compact,系统压力大,造成请求和吞吐的毛刺。
maxlogs的配置多大合适?maxlogs 从90,到120,150,200,250,300,350,400都实验过,越大的hlogs对缓解region写入不均,调大flush尺寸和周期越有利 。
单纯按照流传的公式来看, 下限 50 * 1024/(256*0.95)= 210,上限60 * 1024/(256*0.95)= 252,应该在210到252之间。实际由于各region会陆续触发flush,hlogs即使到达252,memstore总内存可能只有10-20g,依然可以调大。
hlogs上限提高能解决重启后同时flush密集的问题。重启后,各region memstore都是从0开始缓存,hlogs到上限时如果大量region没写满,会触发大量region同时 flush,这些region的memstore再次清零了,下个周期依然有大量region同时flush,打散速度慢于刷新周期触发的flush。hlogs上限出发的同时flush,compact对gc压力很大,请求延时会周期性显著提升。如果256MB flush,8台服务器,hlogs需要到400。128MB则200即可。
hlogs过多可能有什么影响?1.重启时间变长。重试时间长本质是总memstore增大,flush时间长造成的。之前17台规模单台重启大概1分钟,8台规模单台大概1分50秒,总时间并没有增加。再就是如果某台regionserver挂掉,集群要重读未flush的hlogs,hlogs多了会增加重读的量,增加集群负担。

综上所述,最终flush size定为128MB,hlogs上限定为200。

读cache
hfile.block.cache.size 0.19 ->0.2
hbase.bucketcache.size 102400 -> 132000
hbase.bucketcache.percentage.in.combinedcache 0.9 -> 0.85

缓存大小配合堆内堆外内存增加的调整,缓存变成堆外上限112g,堆内20g。L1和L2的比例按实际线上情况由0.9调整为0.85.


compact
hbase.regionserver.thread.compaction.small 6 -> 8
hbase.regionserver.thread.compaction.large 3 -> 4

略微提高minor compact 和major compact速度,尤其是major compact速度,以便机器减少到一半时,夜里能major完。调到12,gc压力过大,所以只是微调。

hdfs
dfs.client.hedged.read.threadpool.size 50 ->300 
dfs.client.hedged.read.threshold.millis 150->500
hbase.regionserver.hlog.slowsync.ms 无->400
compact负载一高,200线程池会报大量的线程池满,资源不够用,所以调到300。
我们用多路读是为了当磁盘故障时,可以读其他副本。如果超时时间太低,可以读本地的去读了远程副本,显著增大集群网络和磁盘IO。读包括compact的读,是轻延时重吞吐的,集群磁盘IO负载高,延时增加,触发多路读又增大了集群的IO压力。尤其是本地化不是100%时,会读其他机器上的副本,400毫秒也依然容易超时,所以超时时间改为500,确保在一般的高负载和非本地化场景中,也不会给集群额外的压力,只有磁盘真正故障堵塞读的时候再读其他副本上。由于95%以上的读都来自内存缓存,500毫秒的最大超时时间并不会造成显著的读请求延时升高,毕竟常态的gc也要几百ms时间。
负载稍微高点,日志文件满屏都是wal log slow,淹没了其他需要关注的问题。由于wal是单线程顺序写单文件,写入速度是有瓶颈的,调到400,只有负载较高时才会打印。

gc参数调整
请看上一篇,本文不再叙述。

未来的工作
近期工作
打散major compact执行时间

现在集群内所有业务分组同一天晚上进行major compact ,集群网络和磁盘IO压力大,存储量也会同时飙升。major compact即将打散到每周7天的晚上,以降低集群的压力。

换compact policy
compact policy优化,需要用户配合做客户端代码优化,和业务方暂定4月初共建。
现在所有业务用的都是默认的default policy,中规中矩。而该项目业务场景非常适合用Date Tiered Compaction,该策略能极大降低compact写放大效应尤其是major compact的压力,并且提升近期热点数据查询速度。
更换该策略,需要业务方略微修改客户端代码,读写时增加TTL的设置,否则可能会降低查询速度。

换压缩算法
换高压缩比算法gz能进一步减少存储总量,已有经验是可以降低30%左右。即存储量视角,可以降到7台服务器规模。换压缩算法会增加cpu利用率,可能对用户读写造成未知的影响,待4月和用户共建。


中远期工作
2副本

好处是存储量立刻降低1/3,坏处是集群同时坏2块盘的概率比坏3块高得多,更容易丢数据。一般是偏离线,稳定性要求不高,存储量偏高的业务,数据在hive有一份,即使有数据缺失可以很快从hive导一份。需要和用户进一步确定业务模式是否能采用。

超卖
目前成本分摊策略,独立业务分组按regionserver服务器数分摊成本,如果独立业务分组存储总量大于分组机器所提供的存储总量,即认为存储量达到瓶颈。就必须要增加机器,不同的业务瓶颈不一样,按存储量严格限制,不利于提升集群资源的整体利用率。
所以从整个集群的资源利用率来看,允许部分吞吐,延时要求不高的业务使用更多的存储,即存储超卖,可以更有效提高集群资源利用率,释放数据引擎潜力,降低业务方使用成本,为公司省钱。
超卖涉及三个维度。超卖存储每TB价格,超卖存储量如何计算。超卖比例,
现在每TB成本是整机打包折算的,而超卖的存储只涉及纯磁盘的折算成本。要超卖存储,需要额外提供超卖存储的每TB价格,这个价格应该比整机的每TB成本低一些。
超卖存储量可以用定时程序收集分组的hdfs存储量,保留最高值,除0.9作为该月实际存储用量,减去分组机器数提供的存储就是超卖量。
超卖比例,前期可以先允许超卖分组存储的20%,并监控集群整体磁盘利用率情况。超卖试运行一段时间后,如果集群存储依然空闲,可以尝试提高超卖比例上限。
目前成本分摊的方式,只支持业务分组按整机打包折算,没有超卖的分摊方式。待评估实际需求后,再来决定是否允许超卖。
如果有业务方需要过高的超卖比例,不适用于现在的集群架构,需要考虑ssd和sata混部的架构。

ssd和sata混部
目前业务按数据量和期望延时的不同分了两个集群,1个是ssd集群,一个是sata盘集群。ssd读写更快,但成本高,存储量有限。
业内最顶尖的做法,是允许ssd和sata盘混部,好处有2。一个是3副本一个是ssd2个是sata盘,保证性能的同时降低存储成本。另一个是时间相关数据按时间分块存储在不同介质上,例如支付宝账单,3个月内ssd,1年内sata盘,一年前的历史数据就存储在更便宜的存储介质上了。历史类数据都适合用这种方式。
混部的方式,在软件,集群架构,机器采购方面都有颇多挑战,是个长期的工作。

(1.3版)hbase优化之旅(三)-regionserver g1 gc执行细节和参数调优方法论详解

hbasechuxiao 发表了文章 • 0 个评论 • 453 次浏览 • 2019-03-14 17:43 • 来自相关话题

release note
1.3版:调整章节顺序,略微修改概述和标题
1.2版:增加目录信息
1.1版 :增加问题12,补充gc问题挂进程和优化方法论总结的章节,解释最终优化内容。
 
 
目录
本文亮点
gc调优效果
学习的起点-知道自己不知道what
g1 gc执行细节
参数调优方式论
增加日志打印
分析统计信息
分析gc日志
gc问题挂进程
1.写入过猛引起的进程挂
2.参数问题导致mixed gc连续出现长时间暂停
优化方法论小结
最终优化内容

 
本文亮点

讲g1原理和参数语义的文章很多,随便谷歌百度一下无数。但大多数都是对官方介绍的翻译转述。很少有文章介绍参数间彼此的影响,调整参数牵一发动全身的影响有哪些。参数调优的方法论,更是没人提及。

本文目标受众是对g1原理和参数有所了解,想进一步了解参数彼此间联系,想在参数调优方面更进一步的同学。

通过一个线上gc优化的实际案例,带你熟悉gc执行细节,了解我总结的参数调优方法论。

如果对g1不了解,可以先搜索了解一下g1原理和参数后再来看本文,会事半功倍。


gc调优效果

线上某分组的regionserver之前存在不稳定的问题。一两个月内总会随机出现1,2台机器,突然cpu飚高,写延时变高引起堆积,hbase日志和监控找不到原因,只能将问题regionserver移走换一台服务器代替。后来发现regionserver直接重启也能解决,怀疑regionserver配置有问题。

经调研和论证,确定是gc参数配置问题,参数优化后彻底解决了该问题。

由于服务器上还部署了datanode,存在一定的资源抢占,regionserver gc时间,间隔有一些波动,但大体是可衡量的。

之前50g堆,young区最大15g,6秒一次young gc,16线程暂停时间100ms左右。 现在100g堆,控制 20g young区,间隔8秒一次young gc,32线程暂停时间大概90ms。

优化前,大概7分钟一次mixed gc周期,优化后大概半小时。

优化前,mixed gc周期内头几次mixed gc之后,mixed gc快速恶化到400-1500毫秒之间,收尾的几次gc基本要秒级以上,gc时间占比短期内达到80%。优化后,mixed gc绝大多数在400ms以内,百分之几的概率在500-900毫秒范围,gc时间基本不会触发超过10%的告警日志打印。


学习的起点-知道自己不知道what 

看过文档,知道参数语义了,就可以进行参数调优了吗?

学习有这么几个阶段,什么都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在“不知道自己不知道”的阶段,误以为自己什么都知道,贸然调优,事倍功半,优化结果南辕北辙。

请看下面的问题,是否有明确的答案?


1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young区起始比例和最大比例的默认值。那么young区有最小比例吗,最小比例是多少?

2.young区的动态大小除了受-XX:MaxGCPauseMillis=100 单次gc最大暂停时间影响,受-XX:G1MaxNewSizePercent=60上限限制,还被其他因素影响吗?

3.-XX:InitiatingHeapOccupancyPercent=45是启动并发mixed gc的已用内存比例阈值,该比例的分母是当前堆内存。那么分子是old区已用,是young+old区已用,还是堆内存已用?

4.一次mixed gc周期内,mixed gc之间的间隔是怎样的,立刻执行下次,有固定时间间隔,还是受其他条件影响?

5.-XX:G1OldCSetRegionThresholdPercent=10 是单次mixed gc扫old区region数的比例。该比例的分母是old区已用region,young+old区已用region,还是堆总region?该比例设置10就会扫10%的region吗,是否受其他条件影响?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目标次数,该数值实际逻辑是什么,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)要回收region的存活对象内存占比,存活越低回收效果越好。如果存活比例超过85%了,这次mixed gc,这次mixed迭代周期内就一定不会回收了吗?

8.很多技术文章都翻译了官方文档的这句话,mixed gc从可回收比例高的region开始回收。实际执行上是怎么做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次数, -XX:G1OldCSetRegionThresholdPercent=10 是最大扫old区region数的比例,-XX:MaxGCPauseMillis=100 是期待的单次gc最大暂停时间。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)是回收region的存活对象内存占比。这几个参数如何决定单次mixed gc清理多少region,一次mixed周期执行多少次mixed gc?

10.其他条件一样,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的单次gc时间短吗?

11.阅读理解,下面三行mixed gc执行日志,说明存在哪些问题?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]
6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]
6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

 
12.坊间传闻,g1内存越大回收效率越差,越不稳定。即使机器资源够分配100g的堆,也只分配50g。这么做有道理吗?

上面各个参数都标注了网上常见的参数解释,了解参数解释你能回答以上的问题吗?如果有疑惑,那么请看下一节。

 


g1 gc执行细节

-XX:G1NewSizePercent同时是young区最小比例。

young区上限还受old区大小影响,最大不超过90%(默认保留10%)-当前old区大小。当old区朝90%接近时,young区会持续减少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old区已用。

mixed gc之间是以触发下限young区gc为间隔的,即下限eden区写满时,mixed gc同时清理young区和old区。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆总region数,100g堆32MB大小region,则总数3200个region,10%就是320个。该比例决定单次mixed gc扫描region数的上限。如果预期扫region时间高于预期剩余时间,则只会扫少量region计算活跃对象比例。

-XX:G1MixedGCCountTarget=8语义是,用触发mixed gc周期时old区的已用region数,除以8,作为每次mixed gc逻辑上最少回收的region数。

一次mixed gc扫描的region中,活跃对象比例高于-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代价高。

如果不昂贵的region数不小于最小回收数,那么回收所有不昂贵region,昂贵region按活跃比例在mixed gc周期内整体排序。如果扫描完所有old区region,垃圾比例依然高于-XX:G1HeapWastePercent比例,那么按活跃对象比逆序清理昂贵region,直到比例降低到阈值。

如果总region数大于最小region数但不昂贵region数不够min,则回收min个region,昂贵region中活跃对象比最低的region填补min的缺。

如果min大于实际扫的region,会回收本次mixed gc所有扫瞄过的region数,即使region的活跃对象比超过阈值。

如果-XX:MaxGCPauseMillis过低,预期扫region时间远大于预期剩余时间,那么实际扫的region会小于min数,即使扫的都是昂贵region,依然会全部回收,造成数秒级gc暂停,实际暂停时间反而比-XX:MaxGCPauseMillis大一些要长。

若干次mixed gc后,如果可回收占堆内存比例小于等于-XX:G1HeapWastePercent,本轮mixed gc周期结束。

 

综上所述,参数有密切的关联关系,调优需要全局权衡。

最后一个问题的日志,由于-XX:MaxGCPauseMillis过低只扫描了少量region,-XX:G1MixedGCCountTarget过低min region数高,昂贵region依然要被回收,暂停时间远大于预期到达秒级,下次扫的region更少,回收昂贵region难以释放内存,持续恶化。堆50g,young区下限2.5g ,间隔不到1秒一次mixed gc,gc时间占比很快超过80%。再加上偶发的memstore内存接近峰值,加上L1读cache,加上静态对象占用的,总不可释放内存比例很高,而-XX:InitiatingHeapOccupancyPercent比例过低,触发mixed gc周期时几乎拷贝了一遍old区所有region,内存也没释放多少空间,regionserver表现出持续的吞吐能力降低,服务不可用现象。
 
目前只遗留了一个问题,g1是否可以用大堆。容我卖个关子,读者可以结合上面的执行细节,先自己认真思考一下,再往下看。

 
参数调优方式论

授人以鱼不如授人以渔,我们先来探讨调优的方法论。

调优方法论是,先整体分析gc运行状况,找到瓶颈点或怀疑有问题的地方。仔细翻阅问题发生时间的gc日志,找到有问题的信息,调优。继续观察。


增加日志打印

调优首先要清楚gc运行状况,上一篇gc探索分享里介绍了如何加打印参数,以及如何通过gceasy可视化统计信息。如果没阅读请先看上一篇相关内容。需要额外注意的是,gceasy对g1支持的有点小bug,gc暂停时间图把mixed gc统计到young gc次数里了。如有图里有暂停时间比-XX:MaxGCPauseMillis高一个数量级的暂停时间,都是mixed gc的。

 
分析统计信息

通过gceasy,我们可以看到gc平均暂停时间,最大暂停时间,应用吞吐占比。gc暂停时间分布,gc前后堆内存变化,gc暂停时间按时间周期的分布图,单次gc垃圾回收内存量的分布图,young区容量分布图,old区容量分布图,young区 promoted分布图,gc内部各阶段时间统计对比,对象创建和升代速度。

通过这些统计信息我们可以了解系统的运行情况,如young 区在多大范围波动,平均young区 gc间隔大概是多久,升代量是多少,mixed gc周期的间隔,每次mixed gc周期回收的old 区内存,等等。掌握这些统计信息,我们就对系统运行情况有了基本了解,可以对系统是否健康做一个初步判断。

不健康主要有两大类,单次暂停时间过长,gc时间占比高。这两个问题又可以引申出单次gc暂停时间过长的次数太多,gc时间占比过高的频率高,mixed gc频率高,每次mixed回收old区内存量太低,等等。

我的经验是,50g或100g的堆内存,如果gc时间占比超过5%,或者gc最大暂停时间超过5秒出现很多次,都是有问题的,需要优化。

如果gc日志跨越好几天,周期性gc时间占比高,但平均gc时间占比可能并不高。如果某一时间段吞吐,延时有问题,可以将这个时间段前后半小时的gc日志截出来,单独上传gceasy。

只看统计信息还不够。本人遇到过日志周期跨越好几天,平均gc时间占比不到2%,最大暂停时间1秒110毫秒,但图表上来看1秒上下的暂停总是连续出现,伴随着周期性业务响应变慢。仔细查看gc日志,gc时间占比在短时间内达到了70%以上,周期性出现。所以需要进一步分析gc日志。


分析gc日志

gc日志内容很多,包括每次gc前堆情况,gc后堆情况,young和mixed gc执行情况。那么我们如何分析呢?

还是要对症下药,找问题出现时前后的日志。

如果暂停时间周期性或偶发出现比预期高一个数量级,可以根据统计信息看到的长时间暂停时间。搜日志,如搜索 real=5找暂停时间5秒到6秒的 ,找到gc上下文,分析为什么慢。


常见以下几种问题

1. max小于等于min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]
2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]
2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

没啥说的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的经验是min最好控制在max的1/4以内,留冗余给昂贵region。

 

2.predicted time is too high

 60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 regions]
 
 60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time:0.00 ms]

 

由于期望gc执行时间短,预期时间太长,只会扫很少的old区region,甚至可能比min region还少,当遇到连续的高代价region,即使是100%活跃的region也要拷贝,执行时间更长,下次预期时间更长,扫的region数更少,进入恶性循环。单次mixed gc释放不了多少内存,gc时间占比越来越高,有zk超时风险。

本质上,这是设置期望时间太短反而造成暂停时间更长,需要放宽期望gc执行时间,减少young 区最小值,以增大回收old区的可用时间。降低-XX:G1OldCSetRegionThresholdPercent比例以降低预期时间。内存使用上,让不可回收内存比例低一些,避免高存活比例region连续出现的概率,即增大堆内存,增大old区回收阈值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低写放大,降低磁盘读写IO,增大L1缓存可以提高读缓存命中率。所以这不单单是gc自己的问题,要系统性综合考虑,确定系统的瓶颈究竟是什么,优化哪个问题更重要。

 

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytes (15.00 %), threshold: 15.00 %]
 
 61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time:0.00 ms]

 
到达垃圾保留比例,最后一次mixed gc只会扫很少的region,如果正好都是昂贵的region,则拷贝代价很高。
运气不好的话,这几乎是不可避免的。调大-XX:G1OldCSetRegionThresholdPercent理论上可以让最后一次扫region的平均数量变大,但会造成predicted time is too high更频繁出现。增加堆内存上限和old区回收阈值,提高-XX:G1HeapWastePercent比例,可以更早结束垃圾mixed gc周期,最后一次扫描都是昂贵region的概率也降低了。调大-XX:G1MixedGCCountTarget 让min region更少,可能每次回收量减少一次回收周期时间拉长,需要配合更高的垃圾浪费率和更低的-XX:G1MixedGCLiveThresholdPercent比例,达到快速清理的效果。


gc问题挂进程

gc问题的极端后果是进程挂掉。一般经验认为,内存增加比释放快,内存不足,full gc, oom,进程就挂了。

我遇到过多次gc引起进程挂掉,但目前还没遇到过g1的oom,甚至都没遇到过g1的full gc。这是因为regionserver内存模型young区升代比例很低,另外g1在恶劣条件下gc时间占比很高,即使regionserver压力很大,还没到full gc,就gc时间占比过高引起zk session超时退出了。下面举两个例子。

1.写入过猛引起的进程挂

业务方补一年的数据,hadoop作业写入过猛,还有热点,flush一直排队,compact一直排队,甚至触发hfile上限堵塞写了。写的p99一度飙升到30秒,young gc一次升old区很多,old区内存增长比正常快。结果还没到old区触发mixed gc,由于young gc达到了1秒2,3次,gc时间占比一度超过了95%,开始出现zk session超时,regionserver自动退出。

可以调大region hfile数上限来缓解,但治标不治本。需要控制用户写入,加quota来限制。

 

2.参数问题导致mixed gc连续出现长时间暂停

regionserver有一定压力,在承受范围内而进程挂了。这是由于参数设置有问题,由于期待暂停时间过低扫的region数不够多,又都是不可回收region,暂停时间越来越长,几次达到8,9秒暂停后,zk session超时退出。

这个按上面的redicted time is too high问题来优化即可。

 

优化方法论小结

1.-XX:ParallelGCThreads官方推荐是逻辑cpu核数的5/8,注意逻辑cpu核数是物理核的2倍,所以24核可以开到32,young和mixed gc确实变快了。

2.参数调整确保每轮mixed gc的max region数是min region数的4倍以上,降低都是昂贵region的几率。

3.适量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暂停时间。目的是留给扫描region充足时间,确保每轮mixed gc扫描的region数和期待的max region数相似。

4.如果不想young gc时间也同步变长,可以通过-XX:G1MaxNewSizePercent降低young区最大比例来控制young gc时间。

5.降低最小young 区比例,可以降低mixed gc时回收young 区的时间,从而增加扫描old区region时间,确保扫描更多region。

6.触发mixed gc周期时,old区可回收内存比例越高,越不容易遇到连续昂贵ergion,回收越有效率。所以应该调大堆内存,调高mixed gc触发阈值,控制不可回收内存比例(即memstore和L1 block cache)。

7.当前面条件都满足时,每次mixed gc周期可回收内存比例很高,每轮mixed gc扫描的region数几倍于min region有充足的region挑选不昂贵ergion,可以调高-XX:G1HeapWastePercent比例让本轮mixed gc尽快结束,降低-XX:G1MixedGCLiveThresholdPercent优先回收活跃对象更少的region。


最终优化内容

-Xmx100g -Xms100g   50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent=   65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

 

 

 
  查看全部
release note
1.3版:调整章节顺序,略微修改概述和标题
1.2版:增加目录信息
1.1版 :增加问题12,补充gc问题挂进程和优化方法论总结的章节,解释最终优化内容。
 
 
目录
本文亮点
gc调优效果
学习的起点-知道自己不知道what
g1 gc执行细节
参数调优方式论
增加日志打印
分析统计信息
分析gc日志
gc问题挂进程
1.写入过猛引起的进程挂
2.参数问题导致mixed gc连续出现长时间暂停
优化方法论小结
最终优化内容

 
本文亮点

讲g1原理和参数语义的文章很多,随便谷歌百度一下无数。但大多数都是对官方介绍的翻译转述。很少有文章介绍参数间彼此的影响,调整参数牵一发动全身的影响有哪些。参数调优的方法论,更是没人提及。

本文目标受众是对g1原理和参数有所了解,想进一步了解参数彼此间联系,想在参数调优方面更进一步的同学。

通过一个线上gc优化的实际案例,带你熟悉gc执行细节,了解我总结的参数调优方法论。

如果对g1不了解,可以先搜索了解一下g1原理和参数后再来看本文,会事半功倍。


gc调优效果

线上某分组的regionserver之前存在不稳定的问题。一两个月内总会随机出现1,2台机器,突然cpu飚高,写延时变高引起堆积,hbase日志和监控找不到原因,只能将问题regionserver移走换一台服务器代替。后来发现regionserver直接重启也能解决,怀疑regionserver配置有问题。

经调研和论证,确定是gc参数配置问题,参数优化后彻底解决了该问题。

由于服务器上还部署了datanode,存在一定的资源抢占,regionserver gc时间,间隔有一些波动,但大体是可衡量的。

之前50g堆,young区最大15g,6秒一次young gc,16线程暂停时间100ms左右。 现在100g堆,控制 20g young区,间隔8秒一次young gc,32线程暂停时间大概90ms。

优化前,大概7分钟一次mixed gc周期,优化后大概半小时。

优化前,mixed gc周期内头几次mixed gc之后,mixed gc快速恶化到400-1500毫秒之间,收尾的几次gc基本要秒级以上,gc时间占比短期内达到80%。优化后,mixed gc绝大多数在400ms以内,百分之几的概率在500-900毫秒范围,gc时间基本不会触发超过10%的告警日志打印。


学习的起点-知道自己不知道what 

看过文档,知道参数语义了,就可以进行参数调优了吗?

学习有这么几个阶段,什么都不知道,不知道自己不知道,知道自己不知道,知道自己知道。

如果在“不知道自己不知道”的阶段,误以为自己什么都知道,贸然调优,事倍功半,优化结果南辕北辙。

请看下面的问题,是否有明确的答案?


1.-XX:G1NewSizePercent=5,-XX:G1MaxNewSizePercent=60 是young区起始比例和最大比例的默认值。那么young区有最小比例吗,最小比例是多少?

2.young区的动态大小除了受-XX:MaxGCPauseMillis=100 单次gc最大暂停时间影响,受-XX:G1MaxNewSizePercent=60上限限制,还被其他因素影响吗?

3.-XX:InitiatingHeapOccupancyPercent=45是启动并发mixed gc的已用内存比例阈值,该比例的分母是当前堆内存。那么分子是old区已用,是young+old区已用,还是堆内存已用?

4.一次mixed gc周期内,mixed gc之间的间隔是怎样的,立刻执行下次,有固定时间间隔,还是受其他条件影响?

5.-XX:G1OldCSetRegionThresholdPercent=10 是单次mixed gc扫old区region数的比例。该比例的分母是old区已用region,young+old区已用region,还是堆总region?该比例设置10就会扫10%的region吗,是否受其他条件影响?

6.-XX:G1MixedGCCountTarget=8 一次mixed gc周期mixed gc的目标次数,该数值实际逻辑是什么,8就是8次mixed gc?

7.-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)要回收region的存活对象内存占比,存活越低回收效果越好。如果存活比例超过85%了,这次mixed gc,这次mixed迭代周期内就一定不会回收了吗?

8.很多技术文章都翻译了官方文档的这句话,mixed gc从可回收比例高的region开始回收。实际执行上是怎么做的?

9.-XX:G1MixedGCCountTarget=8 是一次mixed gc周期mixed gc的次数, -XX:G1OldCSetRegionThresholdPercent=10 是最大扫old区region数的比例,-XX:MaxGCPauseMillis=100 是期待的单次gc最大暂停时间。-XX:G1MixedGCLiveThresholdPercent=85 (其他版本默认值有可能90)是回收region的存活对象内存占比。这几个参数如何决定单次mixed gc清理多少region,一次mixed周期执行多少次mixed gc?

10.其他条件一样,-XX:MaxGCPauseMillis=100 一定比 -XX:MaxGCPauseMillis=200 的单次gc时间短吗?

11.阅读理解,下面三行mixed gc执行日志,说明存在哪些问题?

6949.921: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 6.95 ms, remaining time: 0.00 ms, old: 76 regions, min: 76 regions]
6949.921: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 76 regions, expensive: 70 regions, min: 76 regions, remaining time: 0.00 ms]
6949.921: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 76 regions, predicted pause time: 770.76 ms, target pause time: 100.00 ms]

 
12.坊间传闻,g1内存越大回收效率越差,越不稳定。即使机器资源够分配100g的堆,也只分配50g。这么做有道理吗?

上面各个参数都标注了网上常见的参数解释,了解参数解释你能回答以上的问题吗?如果有疑惑,那么请看下一节。

 


g1 gc执行细节

-XX:G1NewSizePercent同时是young区最小比例。

young区上限还受old区大小影响,最大不超过90%(默认保留10%)-当前old区大小。当old区朝90%接近时,young区会持续减少直到下限。

-XX:InitiatingHeapOccupancyPercent比例的分子是old区已用。

mixed gc之间是以触发下限young区gc为间隔的,即下限eden区写满时,mixed gc同时清理young区和old区。

-XX:G1OldCSetRegionThresholdPercent比例的分母是堆总region数,100g堆32MB大小region,则总数3200个region,10%就是320个。该比例决定单次mixed gc扫描region数的上限。如果预期扫region时间高于预期剩余时间,则只会扫少量region计算活跃对象比例。

-XX:G1MixedGCCountTarget=8语义是,用触发mixed gc周期时old区的已用region数,除以8,作为每次mixed gc逻辑上最少回收的region数。

一次mixed gc扫描的region中,活跃对象比例高于-XX:G1MixedGCLiveThresholdPercent的region,叫做expensive region,清理代价高。

如果不昂贵的region数不小于最小回收数,那么回收所有不昂贵region,昂贵region按活跃比例在mixed gc周期内整体排序。如果扫描完所有old区region,垃圾比例依然高于-XX:G1HeapWastePercent比例,那么按活跃对象比逆序清理昂贵region,直到比例降低到阈值。

如果总region数大于最小region数但不昂贵region数不够min,则回收min个region,昂贵region中活跃对象比最低的region填补min的缺。

如果min大于实际扫的region,会回收本次mixed gc所有扫瞄过的region数,即使region的活跃对象比超过阈值。

如果-XX:MaxGCPauseMillis过低,预期扫region时间远大于预期剩余时间,那么实际扫的region会小于min数,即使扫的都是昂贵region,依然会全部回收,造成数秒级gc暂停,实际暂停时间反而比-XX:MaxGCPauseMillis大一些要长。

若干次mixed gc后,如果可回收占堆内存比例小于等于-XX:G1HeapWastePercent,本轮mixed gc周期结束。

 

综上所述,参数有密切的关联关系,调优需要全局权衡。

最后一个问题的日志,由于-XX:MaxGCPauseMillis过低只扫描了少量region,-XX:G1MixedGCCountTarget过低min region数高,昂贵region依然要被回收,暂停时间远大于预期到达秒级,下次扫的region更少,回收昂贵region难以释放内存,持续恶化。堆50g,young区下限2.5g ,间隔不到1秒一次mixed gc,gc时间占比很快超过80%。再加上偶发的memstore内存接近峰值,加上L1读cache,加上静态对象占用的,总不可释放内存比例很高,而-XX:InitiatingHeapOccupancyPercent比例过低,触发mixed gc周期时几乎拷贝了一遍old区所有region,内存也没释放多少空间,regionserver表现出持续的吞吐能力降低,服务不可用现象。
 
目前只遗留了一个问题,g1是否可以用大堆。容我卖个关子,读者可以结合上面的执行细节,先自己认真思考一下,再往下看。

 
参数调优方式论

授人以鱼不如授人以渔,我们先来探讨调优的方法论。

调优方法论是,先整体分析gc运行状况,找到瓶颈点或怀疑有问题的地方。仔细翻阅问题发生时间的gc日志,找到有问题的信息,调优。继续观察。


增加日志打印

调优首先要清楚gc运行状况,上一篇gc探索分享里介绍了如何加打印参数,以及如何通过gceasy可视化统计信息。如果没阅读请先看上一篇相关内容。需要额外注意的是,gceasy对g1支持的有点小bug,gc暂停时间图把mixed gc统计到young gc次数里了。如有图里有暂停时间比-XX:MaxGCPauseMillis高一个数量级的暂停时间,都是mixed gc的。

 
分析统计信息

通过gceasy,我们可以看到gc平均暂停时间,最大暂停时间,应用吞吐占比。gc暂停时间分布,gc前后堆内存变化,gc暂停时间按时间周期的分布图,单次gc垃圾回收内存量的分布图,young区容量分布图,old区容量分布图,young区 promoted分布图,gc内部各阶段时间统计对比,对象创建和升代速度。

通过这些统计信息我们可以了解系统的运行情况,如young 区在多大范围波动,平均young区 gc间隔大概是多久,升代量是多少,mixed gc周期的间隔,每次mixed gc周期回收的old 区内存,等等。掌握这些统计信息,我们就对系统运行情况有了基本了解,可以对系统是否健康做一个初步判断。

不健康主要有两大类,单次暂停时间过长,gc时间占比高。这两个问题又可以引申出单次gc暂停时间过长的次数太多,gc时间占比过高的频率高,mixed gc频率高,每次mixed回收old区内存量太低,等等。

我的经验是,50g或100g的堆内存,如果gc时间占比超过5%,或者gc最大暂停时间超过5秒出现很多次,都是有问题的,需要优化。

如果gc日志跨越好几天,周期性gc时间占比高,但平均gc时间占比可能并不高。如果某一时间段吞吐,延时有问题,可以将这个时间段前后半小时的gc日志截出来,单独上传gceasy。

只看统计信息还不够。本人遇到过日志周期跨越好几天,平均gc时间占比不到2%,最大暂停时间1秒110毫秒,但图表上来看1秒上下的暂停总是连续出现,伴随着周期性业务响应变慢。仔细查看gc日志,gc时间占比在短时间内达到了70%以上,周期性出现。所以需要进一步分析gc日志。


分析gc日志

gc日志内容很多,包括每次gc前堆情况,gc后堆情况,young和mixed gc执行情况。那么我们如何分析呢?

还是要对症下药,找问题出现时前后的日志。

如果暂停时间周期性或偶发出现比预期高一个数量级,可以根据统计信息看到的长时间暂停时间。搜日志,如搜索 real=5找暂停时间5秒到6秒的 ,找到gc上下文,分析为什么慢。


常见以下几种问题

1. max小于等于min

2194074.642: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: old CSet region num reached max, old: 80 regions, max: 80 regions]
2194074.642: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 80 regions, expensive: 62 regions, min: 105 regions, remaining time: 0.00 ms]
2194074.642: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 79 regions, survivors: 1 regions, old: 80 regions, predicted pause time: 374.58 ms, target pause time: 100.00 ms]

没啥说的,改大-XX:G1MixedGCCountTarget,提高max/min的比例吧。我的经验是min最好控制在max的1/4以内,留冗余给昂贵region。

 

2.predicted time is too high

 60998.873: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: predicted time is too high, predicted time: 308.81 ms, remaining time: 0.00 ms, old: 28 regions, min: 28 regions]
 
 60998.873: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 28 regions, expensive: 27 regions, min: 28 regions, remaining time:0.00 ms]

 

由于期望gc执行时间短,预期时间太长,只会扫很少的old区region,甚至可能比min region还少,当遇到连续的高代价region,即使是100%活跃的region也要拷贝,执行时间更长,下次预期时间更长,扫的region数更少,进入恶性循环。单次mixed gc释放不了多少内存,gc时间占比越来越高,有zk超时风险。

本质上,这是设置期望时间太短反而造成暂停时间更长,需要放宽期望gc执行时间,减少young 区最小值,以增大回收old区的可用时间。降低-XX:G1OldCSetRegionThresholdPercent比例以降低预期时间。内存使用上,让不可回收内存比例低一些,避免高存活比例region连续出现的概率,即增大堆内存,增大old区回收阈值,控制memstore,block cache L1的尺寸。要注意的是,memstore增大可以降低写放大,降低磁盘读写IO,增大L1缓存可以提高读缓存命中率。所以这不单单是gc自己的问题,要系统性综合考虑,确定系统的瓶颈究竟是什么,优化哪个问题更重要。

 

3.reclaimable percentage not over threshold

61007.913: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 24 regions, max: 320 regions, reclaimable: 16101191472 bytes (15.00 %), threshold: 15.00 %]
 
 61007.913: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 24 regions, expensive: 24 regions, min: 28 regions, remaining time:0.00 ms]

 
到达垃圾保留比例,最后一次mixed gc只会扫很少的region,如果正好都是昂贵的region,则拷贝代价很高。
运气不好的话,这几乎是不可避免的。调大-XX:G1OldCSetRegionThresholdPercent理论上可以让最后一次扫region的平均数量变大,但会造成predicted time is too high更频繁出现。增加堆内存上限和old区回收阈值,提高-XX:G1HeapWastePercent比例,可以更早结束垃圾mixed gc周期,最后一次扫描都是昂贵region的概率也降低了。调大-XX:G1MixedGCCountTarget 让min region更少,可能每次回收量减少一次回收周期时间拉长,需要配合更高的垃圾浪费率和更低的-XX:G1MixedGCLiveThresholdPercent比例,达到快速清理的效果。


gc问题挂进程

gc问题的极端后果是进程挂掉。一般经验认为,内存增加比释放快,内存不足,full gc, oom,进程就挂了。

我遇到过多次gc引起进程挂掉,但目前还没遇到过g1的oom,甚至都没遇到过g1的full gc。这是因为regionserver内存模型young区升代比例很低,另外g1在恶劣条件下gc时间占比很高,即使regionserver压力很大,还没到full gc,就gc时间占比过高引起zk session超时退出了。下面举两个例子。

1.写入过猛引起的进程挂

业务方补一年的数据,hadoop作业写入过猛,还有热点,flush一直排队,compact一直排队,甚至触发hfile上限堵塞写了。写的p99一度飙升到30秒,young gc一次升old区很多,old区内存增长比正常快。结果还没到old区触发mixed gc,由于young gc达到了1秒2,3次,gc时间占比一度超过了95%,开始出现zk session超时,regionserver自动退出。

可以调大region hfile数上限来缓解,但治标不治本。需要控制用户写入,加quota来限制。

 

2.参数问题导致mixed gc连续出现长时间暂停

regionserver有一定压力,在承受范围内而进程挂了。这是由于参数设置有问题,由于期待暂停时间过低扫的region数不够多,又都是不可回收region,暂停时间越来越长,几次达到8,9秒暂停后,zk session超时退出。

这个按上面的redicted time is too high问题来优化即可。

 

优化方法论小结

1.-XX:ParallelGCThreads官方推荐是逻辑cpu核数的5/8,注意逻辑cpu核数是物理核的2倍,所以24核可以开到32,young和mixed gc确实变快了。

2.参数调整确保每轮mixed gc的max region数是min region数的4倍以上,降低都是昂贵region的几率。

3.适量增加-XX:MaxGCPauseMillis反而可以降低mixed gc的暂停时间。目的是留给扫描region充足时间,确保每轮mixed gc扫描的region数和期待的max region数相似。

4.如果不想young gc时间也同步变长,可以通过-XX:G1MaxNewSizePercent降低young区最大比例来控制young gc时间。

5.降低最小young 区比例,可以降低mixed gc时回收young 区的时间,从而增加扫描old区region时间,确保扫描更多region。

6.触发mixed gc周期时,old区可回收内存比例越高,越不容易遇到连续昂贵ergion,回收越有效率。所以应该调大堆内存,调高mixed gc触发阈值,控制不可回收内存比例(即memstore和L1 block cache)。

7.当前面条件都满足时,每次mixed gc周期可回收内存比例很高,每轮mixed gc扫描的region数几倍于min region有充足的region挑选不昂贵ergion,可以调高-XX:G1HeapWastePercent比例让本轮mixed gc尽快结束,降低-XX:G1MixedGCLiveThresholdPercent优先回收活跃对象更少的region。


最终优化内容

-Xmx100g -Xms100g   50g -> 100g
-XX:MaxDirectMemorySize= 100g -> 120g
-XX:ConcGCThreads= 4 -> 8
-XX:ParallelGCThreads= 16 -> 32
-XX:G1NewSizePercent= 5 -> 3
-XX:G1MaxNewSizePercent= 60 -> 20
-XX:G1MixedGCCountTarget= 8 -> 64
-XX:G1OldCSetRegionThresholdPercent= 10 -> 4
-XX:InitiatingHeapOccupancyPercent=   65 ->80
-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

-XX:G1HeapWastePercent= 5 -> 20
-XX:G1MixedGCLiveThresholdPercent= 85 -> 80

 

 

 
 

hbase优化之旅(二)regionserver的G1 GC优化探索

hbasechuxiao 发表了文章 • 1 个评论 • 547 次浏览 • 2019-03-06 17:09 • 来自相关话题

优化的最终目的是保障现有用户体验的同时,减少机器,节约成本。

为了更好的编写本文和后续文章,花费20美金。欢迎去附录链接专栏赞赏支持。

g1介绍
g1特点

g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。

g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在预期范围内。

g1适用场景

对平均响应时间,最大响应时间有严格要求的应用系统,如hbase regionserver。

优化原则
先优化业务层和应用层

系统调优是从业务到实现,从整体到局部,从架构到具体组件的。在进行gc调优之前,我们应该确保业务层和应用层已经评估优化过。业务层和应用层的优化一般来说更容易有收益,我们不能指望一个架构设计有缺陷,应用层代码有很多已知问题的系统,通过gc调优一劳永逸。

gc调优3选2原则

先来看一下衡量gc的指标有哪些。对应用吞吐量的影响(一般是gc对cpu的消耗),对延迟的影响,总内存占用(gc触发时留有内存业务可以继续,留有内存做对象拷贝碎片整理等操作,不能oom)。

GC调优3选2原则: 在吞吐量、延迟、内存占用上,我们只能选择其中两个进行调优,无法三者兼得。

hbase已有业务regionserver的调优目标

在调优之前,必须要有明确的性能优化目标, 然后找到未达到该目标的性能瓶颈。再针对瓶颈做优化。通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

hbase集群启用了group分组,重要业务有独立的regionserver分组。

重要业务regionserver的调优目标是,在满足业务延迟要求的基础上,用尽量低的成本,满足业务吞吐量的峰值需求。

也就是说,总吞吐量固定,延迟要求固定,单机cpu和内存固定,求最小机器数。

再转换一下,对单机来说,延迟指标确定,将单机吞吐在单机cpu和内存允许的范围内调整到最大。

需要说明的是,单机能承担多少吞吐,跟业务访问模型,region数,读写缓存参数,网络IO,磁盘IO都有关系。业务和hbase参数的调整应该在gc优化之前进行,网络和磁盘IO一般是应用层优化的。所以下文假设业务层和应用层已优化完毕,网络和磁盘都不是瓶颈,只聚焦在gc参数调优。

本文假设我们换算后的延迟目标是平均gc暂停时间100ms,最大暂停时间2s,gc时间占比3%以内。实际达到这个目标后,还要通过regionserver监控确定请求的延时要是否在用户用户的要求范围内。

影响延迟的因素

gc的时间占比。平均stw gc时间,频率。毛刺stw gc时间,频率。峰值stw gc时间,频率。

一般来说,regionserver应该避免full gc。

新生代越大,单次young gc时间越长,频率越低。

mixed gc受gc触发时机,gc并发线程数,预期迭代次数,每个迭代回收分片比例等多个参数影响,详见附录官方文档。

关于JVM版本

目前生产环境用1.8.0_77, 小米hbase环境用1.8.0_111, Oracle jdk的8最新版本是8u201。

intel性能测试见附录,jdk7不同版本间g1性能差距很大。Jdk7u21升级到jdk7u60,gc以及stw gc的平均时间,最大时间,时间分布都有大幅优化。

所以应该尽量用最新版本的JDK。

优化步骤

需要有方法论判断当前是否应该继续优化。

根据业务对延迟的需求,比较现在的请求延迟和gc情况,预估能接受的平均gc暂停时间,最大gc 暂停时间范围。

关掉自动balance,给一台regionserver少量增加region从而增加单机吞吐。当请求延迟超过用户要求的警戒线后,分析gc日志,找到瓶颈,优化降低gc延迟从而降低请求延迟,以便继续增加region。

当单机region数过多(可以考虑合并region),cpu负载过高,请求延迟无法降下来,任意一个条件满足,单机优化结束。稳定运行一段时间后,尝试将优化推广到整个业务分组。

增加日志


要分析gc情况一定要有gc日志。之前的日志参数如下-XX:+PrintGCDetails gc细节
-XX:+PrintGCDateStamps 时间戳
-Xloggc:${HBASE_LOG_DIR}/gc-`date +'%Y%m%d%H%M'` gc文件格式
-XX:+UseGCLogFileRotation gc文件循环
-XX:NumberOfGCLogFiles=10 文件数
-XX:GCLogFileSize=512M 文件大小
-XX:+HeapDumpOnOutOfMemoryError oom时堆dump
-XX:HeapDumpPath=${HBASE_LOG_DIR}/hbase.heapdump dump目录
-XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
-XX:+PrintAdaptiveSizePolicy 打印自适应收集的大小
-XX:+PrintFlagsFinal 打印参数值
参考其他优化的文章,增加打印参数-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间
-XX:+PrintTenuringDistribution https://www.jianshu.com/p/e634955f3bbb survivor分布情况
-XX:+PrintHeapAtGC gc前后打印堆信息
-XX:+PrintSafepointStatistics https://blog.csdn.net/u0119182 ... 47159 分析安全点统计信息,优化gc参考
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1 打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks。似乎cms更有用
gc日志太多可能会影响性能,目前没有日志对性能影响的数据,暂不考虑日志对性能的影响。


可视化

有很多gc可视化的工具。比如在线的gceasy https://gceasy.io/index.jsp#banner,上传gc日志可以进行分析。免费功能有各种图表展示。20美金一个月可以使用高级功能。

本文用gceasy做例子,其他可视化工具介绍见附录。

下面从前文优化过的节点开始,分析gc日志。








 
问题分析
gceasy优化建议

不花钱怎么变强?开启gceasy高级功能。


单次gc暂停最长到5秒了。链接给出了优化建议,见附录。

首先是优化程序降低对象创建速度。现在平均对象创建速度是1.22gb/sec。减少对象创建速度主要是更多用堆外内存,优化写放大,本文重点是gc优化,不展开。

再是年轻代太小,增大年轻代。这规则适用于其他gc,而g1 young gc是动态的,mixed gc是分次迭代的,young gc不能太大,否则young gc反而会比单次mixed慢。

再就是设置期待的最大暂停时间,目前设置100ms,暂不修改。

服务器上进程多,内存交换,服务器无这个情况。

gc线程不够多,有可能。但gc3秒以上的只有6次,差不多每天一次。如果是线程数明显不够次数应该更多,先放放。

服务器高IO导致的等待时间长。有可能,有datanode,需要进一步分析gc日志和IO监控。

没有禁用system.gc()。gc case表格来看没有相关调用。

堆太大。有可能,毕竟80GB堆。但大堆对吞吐有好处。

看5秒暂停gc的具体日志,{Heap before GC invocations=50103 (full 0):
garbage-first heap total 83886080K, used 48760053K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 5 survivors (163840K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
2019-02-18T17:48:53.570+0800: 365568.567: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 268435456 bytes, new threshold 1 (max 1)
- age 1: 87997400 bytes, 87997400 total
365568.567: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 3313913, predicted base time: 1151.36 ms, remaining time: 0.00 ms, target pause time: 100.00 ms]
365568.567: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 123 regions, survivors: 5 regions, predicted young region time: 50.17 ms]
365568.583: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 70 regions, max: 256 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
365568.583: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 70 regions, expensive: 70 regions, min: 80 regions, remaining time: 0.00 ms]
365568.583: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 123 regions, survivors: 5 regions, old: 70 regions, predicted pause time: 4353.85 ms, target pause time: 100.00 ms]
365573.866: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 40.96 %, threshold: 10.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
365573.866: [G1Ergonomics (Mixed GCs) do not continue mixed GCs, reason: reclaimable percentage not over threshold, candidate old regions: 407 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
, 5.2999017 secs]
[Parallel Time: 5227.7 ms, GC Workers: 16]
[GC Worker Start (ms): Min: 365568584.3, Avg: 365568584.3, Max: 365568584.4, Diff: 0.1]
[Ext Root Scanning (ms): Min: 1.3, Avg: 1.6, Max: 3.9, Diff: 2.7, Sum: 25.1]
[Update RS (ms): Min: 1065.2, Avg: 1067.2, Max: 1067.8, Diff: 2.6, Sum: 17075.4]
[Processed Buffers: Min: 739, Avg: 831.5, Max: 879, Diff: 140, Sum: 13304]
[Scan RS (ms): Min: 3610.8, Avg: 3611.6, Max: 3612.6, Diff: 1.8, Sum: 57786.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.3, Diff: 0.3, Sum: 0.5]
[Object Copy (ms): Min: 545.2, Avg: 546.3, Max: 547.2, Diff: 2.0, Sum: 8741.5]
[Termination (ms): Min: 0.1, Avg: 0.6, Max: 0.8, Diff: 0.7, Sum: 9.8]
[Termination Attempts: Min: 1, Avg: 465.3, Max: 552, Diff: 551, Sum: 7445]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 5227.3, Avg: 5227.4, Max: 5227.5, Diff: 0.2, Sum: 83638.8]
[GC Worker End (ms): Min: 365573811.8, Avg: 365573811.8, Max: 365573811.8, Diff: 0.0]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 3.4 ms]
[Other: 68.4 ms]
[Choose CSet: 16.3 ms]
[Ref Proc: 3.6 ms]
[Ref Enq: 0.2 ms]
[Redirty Cards: 8.7 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 31.6 ms]
[Eden: 3936.0M(3936.0M)->0.0B(3904.0M) Survivors: 160.0M->192.0M Heap: 46.5G(80.0G)->42.3G(80.0G)]
Heap after GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 44323410K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 6 young (196608K), 6 survivors (196608K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
}
[Times: user=85.30 sys=0.00, real=5.30 secs]
2019-02-18T17:48:58.871+0800: 365573.867: Total time for which application threads were stopped: 5.3131110 seconds, Stopping threads took: 0.0005771 seconds
2019-02-18T17:48:58.896+0800: 365573.893: Total time for which application threads were stopped: 0.0186168 seconds, Stopping threads took: 0.0005541 seconds
2019-02-18T17:48:58.906+0800: 365573.902: Total time for which application threads were stopped: 0.0078098 seconds, Stopping threads took: 0.0005988 seconds
2019-02-18T17:48:58.915+0800: 365573.912: Total time for which application threads were stopped: 0.0075271 seconds, Stopping threads took: 0.0003996 seconds
2019-02-18T17:48:58.923+0800: 365573.919: Total time for which application threads were stopped: 0.0072014 seconds, Stopping threads took: 0.0003114 seconds
2019-02-18T17:48:58.934+0800: 365573.930: Total time for which application threads were stopped: 0.0078706 seconds, Stopping threads took: 0.0005490 seconds
2019-02-18T17:48:58.942+0800: 365573.938: Total time for which application threads were stopped: 0.0073198 seconds, Stopping threads took: 0.0003927 seconds
2019-02-18T17:48:58.964+0800: 365573.960: Total time for which application threads were stopped: 0.0078810 seconds, Stopping threads took: 0.0007641 seconds
2019-02-18T17:48:58.972+0800: 365573.969: Total time for which application threads were stopped: 0.0075881 seconds, Stopping threads took: 0.0006277 seconds
2019-02-18T17:48:58.981+0800: 365573.978: Total time for which application threads were stopped: 0.0081290 seconds, Stopping threads took: 0.0011246 seconds
2019-02-18T17:48:58.992+0800: 365573.989: Total time for which application threads were stopped: 0.0076556 seconds, Stopping threads took: 0.0005358 seconds
2019-02-18T17:48:59.015+0800: 365574.011: Total time for which application threads were stopped: 0.0076750 seconds, Stopping threads took: 0.0005602 seconds
2019-02-18T17:48:59.026+0800: 365574.022: Total time for which application threads were stopped: 0.0089086 seconds, Stopping threads took: 0.0006000 seconds
2019-02-18T17:48:59.044+0800: 365574.041: Total time for which application threads were stopped: 0.0087554 seconds, Stopping threads took: 0.0006332 seconds
2019-02-18T17:48:59.054+0800: 365574.050: Total time for which application threads were stopped: 0.0084038 seconds, Stopping threads took: 0.0004326 seconds
{Heap before GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 48321106K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 6 survivors (196608K)
Metaspace used 50946K, capacity 51410K, committed 52168K, reserved 53248K
从日志看,期望survivor区有大概256MB,实际只有32*5=160MB,可能会将eden区直接提到old区。

近期gc时间占比过高,到40%了。

清理young 区的gc线程耗时过长,清理后可回收内存低于10%,mixed gc结束,其他gc线程空闲。说明mixed gc触发阈值设置低了,young gc参数也需要调整。

看暂停时间大于2秒的其他日志,都是触发mixed gc,但只有一个清理young和个别old区region的线程工作。确实说明mixed gc触发阈值设置低了,young gc过大了。
 
 


有41次gc的系统时间大于用户时间,大概3小时一次。

操作系统层面有可能是操作系统层异常,或者系统太忙了。出现频率大概3小时一次,看ganglia日志问题出现时cpu负载并不高。并且系统时间只是略高于用户时间不是明显有问题的数量级差距。先不考虑操作系统和JVM有bug的情况,暂不处理。

 

还是说有5秒的gc,同上。


修改参数-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UnlockExperimentalVMOptions
-XX:+ParallelRefProcEnabled
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=16
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=20
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=32m
-XX:G1MixedGCCountTarget=16
-XX:InitiatingHeapOccupancyPercent=60
-XX:MaxDirectMemorySize=110g
-XX:G1OldCSetRegionThresholdPercent=10
-XX:G1HeapWastePercent=10
-Xmx80g
-Xms80g

从gc前后堆内存图来看,-XX:InitiatingHeapOccupancyPercent=65,52GB更合适。

暂停时间平均135ms,从暂停时间图来看,young gc时间较长,需要调小young gc区,小米经验改初始值,由5改成2。-XX:G1NewSizePercent=2

mixed gc 大多数一轮不到8次,暂停时间基本都超过100ms了。期待每轮16次的参数没起作用,这是由于每轮清理的region数过多。-XX:G1OldCSetRegionThresholdPercent=5,由10改成5,减少单次mixed gc时间。

survivor区应该大于young日志中期待的,Set SurvivorRatio from 8 (default) to 4。-XX:InitiatingHeapOccupancyPercent=65
-XX:G1NewSizePercent=2
-XX:G1OldCSetRegionThresholdPercent=5
-XX:SurvivorRatio=4


设置其他必要参数

参考各类优化资料,增加以下参数-XX:+UseStringDeduplication 字符串去重,提高性能
-XX:-ResizePLAB 减少gc线程间通信的东西,关闭动态提升本地buffer
-XX:+PerfDisableSharedMem 关掉统计信息的内存映射。开启在某些特殊场景下,会极大增加gc暂停时间
优化结果




平均暂停时间,最大暂停时间,暂停时间分布,gc吞吐占比都有提高,优化成功。

接下来迭代提高这台服务器region数,做gc优化。

 
遗留问题

偶发系统时间大于用户时间的问题,依然存在。发生时系统负载并不高,有可能操作系统和JVM层面有优化空间。

gceasy建议不要用-XX:+UseGCLogFileRotation,理由是历史会看不到,文件顺序不容易分辨,多个文件不利于gc分析。但这个参数意义是控制总文件大小,所以依然保留。

附录
g1官方介绍

https://www.oracle.com/technet ... .html

其他可视化工具介绍

https://blog.csdn.net/qq_32447 ... 67984

https://blog.csdn.net/xuelinme ... 90115

https://blog.csdn.net/renfufei ... 78064

gceasy优化建议

https://blog.gceasy.io/2016/11 ... uses/

https://blog.gceasy.io/2016/12 ... time/

https://blog.gceasy.io/2016/12 ... pped/

https://blog.gceasy.io/2018/12 ... tion/

https://blog.gceasy.io/2019/01 ... tion/

优化参考

https://www.oracle.com/technet ... .html

https://blogs.apache.org/hbase ... hbase

https://software.intel.com/en- ... hbase

http://openinx.github.io/ppt/h ... 8.pdf

https://blog.csdn.net/songhaif ... 77612

https://blog.csdn.net/maosijun ... 62489

https://dzone.com/articles/g1g ... ags-1

http://www.evanjones.ca/jvm-mmap-pause.html

之前的优化探索
 http://www.hbase.group/article/192
 
专栏地址
 
https://zhuanlan.zhihu.com/c_178811296 查看全部
优化的最终目的是保障现有用户体验的同时,减少机器,节约成本。

为了更好的编写本文和后续文章,花费20美金。欢迎去附录链接专栏赞赏支持。

g1介绍
g1特点


g1原理见附录官方文档,本文假设读者对jvm gc和g1原理有基本的了解。

g1特点是内存分片(一般1024片),支持动态调整young区大小,old区使用mixed gc方式分成多次小gc,尽量减少单次gc STW(stop the world)暂停时间,让gc对应用延迟的影响在预期范围内。

g1适用场景

对平均响应时间,最大响应时间有严格要求的应用系统,如hbase regionserver。

优化原则
先优化业务层和应用层


系统调优是从业务到实现,从整体到局部,从架构到具体组件的。在进行gc调优之前,我们应该确保业务层和应用层已经评估优化过。业务层和应用层的优化一般来说更容易有收益,我们不能指望一个架构设计有缺陷,应用层代码有很多已知问题的系统,通过gc调优一劳永逸。

gc调优3选2原则

先来看一下衡量gc的指标有哪些。对应用吞吐量的影响(一般是gc对cpu的消耗),对延迟的影响,总内存占用(gc触发时留有内存业务可以继续,留有内存做对象拷贝碎片整理等操作,不能oom)。

GC调优3选2原则: 在吞吐量、延迟、内存占用上,我们只能选择其中两个进行调优,无法三者兼得。

hbase已有业务regionserver的调优目标

在调优之前,必须要有明确的性能优化目标, 然后找到未达到该目标的性能瓶颈。再针对瓶颈做优化。通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

hbase集群启用了group分组,重要业务有独立的regionserver分组。

重要业务regionserver的调优目标是,在满足业务延迟要求的基础上,用尽量低的成本,满足业务吞吐量的峰值需求。

也就是说,总吞吐量固定,延迟要求固定,单机cpu和内存固定,求最小机器数。

再转换一下,对单机来说,延迟指标确定,将单机吞吐在单机cpu和内存允许的范围内调整到最大。

需要说明的是,单机能承担多少吞吐,跟业务访问模型,region数,读写缓存参数,网络IO,磁盘IO都有关系。业务和hbase参数的调整应该在gc优化之前进行,网络和磁盘IO一般是应用层优化的。所以下文假设业务层和应用层已优化完毕,网络和磁盘都不是瓶颈,只聚焦在gc参数调优。

本文假设我们换算后的延迟目标是平均gc暂停时间100ms,最大暂停时间2s,gc时间占比3%以内。实际达到这个目标后,还要通过regionserver监控确定请求的延时要是否在用户用户的要求范围内。

影响延迟的因素

gc的时间占比。平均stw gc时间,频率。毛刺stw gc时间,频率。峰值stw gc时间,频率。

一般来说,regionserver应该避免full gc。

新生代越大,单次young gc时间越长,频率越低。

mixed gc受gc触发时机,gc并发线程数,预期迭代次数,每个迭代回收分片比例等多个参数影响,详见附录官方文档。

关于JVM版本

目前生产环境用1.8.0_77, 小米hbase环境用1.8.0_111, Oracle jdk的8最新版本是8u201。

intel性能测试见附录,jdk7不同版本间g1性能差距很大。Jdk7u21升级到jdk7u60,gc以及stw gc的平均时间,最大时间,时间分布都有大幅优化。

所以应该尽量用最新版本的JDK。

优化步骤

需要有方法论判断当前是否应该继续优化。

根据业务对延迟的需求,比较现在的请求延迟和gc情况,预估能接受的平均gc暂停时间,最大gc 暂停时间范围。

关掉自动balance,给一台regionserver少量增加region从而增加单机吞吐。当请求延迟超过用户要求的警戒线后,分析gc日志,找到瓶颈,优化降低gc延迟从而降低请求延迟,以便继续增加region。

当单机region数过多(可以考虑合并region),cpu负载过高,请求延迟无法降下来,任意一个条件满足,单机优化结束。稳定运行一段时间后,尝试将优化推广到整个业务分组。

增加日志


要分析gc情况一定要有gc日志。之前的日志参数如下
-XX:+PrintGCDetails gc细节
-XX:+PrintGCDateStamps 时间戳
-Xloggc:${HBASE_LOG_DIR}/gc-`date +'%Y%m%d%H%M'` gc文件格式
-XX:+UseGCLogFileRotation gc文件循环
-XX:NumberOfGCLogFiles=10 文件数
-XX:GCLogFileSize=512M 文件大小
-XX:+HeapDumpOnOutOfMemoryError oom时堆dump
-XX:HeapDumpPath=${HBASE_LOG_DIR}/hbase.heapdump dump目录
-XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
-XX:+PrintAdaptiveSizePolicy 打印自适应收集的大小
-XX:+PrintFlagsFinal 打印参数值

参考其他优化的文章,增加打印参数
-XX:+PrintGCApplicationStoppedTime    打印垃圾回收期间程序暂停的时间
-XX:+PrintTenuringDistribution https://www.jianshu.com/p/e634955f3bbb survivor分布情况
-XX:+PrintHeapAtGC gc前后打印堆信息
-XX:+PrintSafepointStatistics https://blog.csdn.net/u0119182 ... 47159 分析安全点统计信息,优化gc参考
-XX:PrintSafepointStatisticsCount=1
-XX:PrintFLSStatistics=1 打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free Space、Max Chunk Size和Num Chunks。似乎cms更有用

gc日志太多可能会影响性能,目前没有日志对性能影响的数据,暂不考虑日志对性能的影响。


可视化

有很多gc可视化的工具。比如在线的gceasy https://gceasy.io/index.jsp#banner,上传gc日志可以进行分析。免费功能有各种图表展示。20美金一个月可以使用高级功能。

本文用gceasy做例子,其他可视化工具介绍见附录。

下面从前文优化过的节点开始,分析gc日志。








 
问题分析
gceasy优化建议


不花钱怎么变强?开启gceasy高级功能。


单次gc暂停最长到5秒了。链接给出了优化建议,见附录。

首先是优化程序降低对象创建速度。现在平均对象创建速度是1.22gb/sec。减少对象创建速度主要是更多用堆外内存,优化写放大,本文重点是gc优化,不展开。

再是年轻代太小,增大年轻代。这规则适用于其他gc,而g1 young gc是动态的,mixed gc是分次迭代的,young gc不能太大,否则young gc反而会比单次mixed慢。

再就是设置期待的最大暂停时间,目前设置100ms,暂不修改。

服务器上进程多,内存交换,服务器无这个情况。

gc线程不够多,有可能。但gc3秒以上的只有6次,差不多每天一次。如果是线程数明显不够次数应该更多,先放放。

服务器高IO导致的等待时间长。有可能,有datanode,需要进一步分析gc日志和IO监控。

没有禁用system.gc()。gc case表格来看没有相关调用。

堆太大。有可能,毕竟80GB堆。但大堆对吞吐有好处。

看5秒暂停gc的具体日志,
{Heap before GC invocations=50103 (full 0):
garbage-first heap total 83886080K, used 48760053K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 5 survivors (163840K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
2019-02-18T17:48:53.570+0800: 365568.567: [GC pause (G1 Evacuation Pause) (mixed)
Desired survivor size 268435456 bytes, new threshold 1 (max 1)
- age 1: 87997400 bytes, 87997400 total
365568.567: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 3313913, predicted base time: 1151.36 ms, remaining time: 0.00 ms, target pause time: 100.00 ms]
365568.567: [G1Ergonomics (CSet Construction) add young regions to CSet, eden: 123 regions, survivors: 5 regions, predicted young region time: 50.17 ms]
365568.583: [G1Ergonomics (CSet Construction) finish adding old regions to CSet, reason: reclaimable percentage not over threshold, old: 70 regions, max: 256 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
365568.583: [G1Ergonomics (CSet Construction) added expensive regions to CSet, reason: old CSet region num not reached min, old: 70 regions, expensive: 70 regions, min: 80 regions, remaining time: 0.00 ms]
365568.583: [G1Ergonomics (CSet Construction) finish choosing CSet, eden: 123 regions, survivors: 5 regions, old: 70 regions, predicted pause time: 4353.85 ms, target pause time: 100.00 ms]
365573.866: [G1Ergonomics (Heap Sizing) attempt heap expansion, reason: recent GC overhead higher than threshold after GC, recent GC overhead: 40.96 %, threshold: 10.00 %, uncommitted: 0 bytes, calculated expansion amount: 0 bytes (20.00 %)]
365573.866: [G1Ergonomics (Mixed GCs) do not continue mixed GCs, reason: reclaimable percentage not over threshold, candidate old regions: 407 regions, reclaimable: 8557735352 bytes (9.96 %), threshold: 10.00 %]
, 5.2999017 secs]
[Parallel Time: 5227.7 ms, GC Workers: 16]
[GC Worker Start (ms): Min: 365568584.3, Avg: 365568584.3, Max: 365568584.4, Diff: 0.1]
[Ext Root Scanning (ms): Min: 1.3, Avg: 1.6, Max: 3.9, Diff: 2.7, Sum: 25.1]
[Update RS (ms): Min: 1065.2, Avg: 1067.2, Max: 1067.8, Diff: 2.6, Sum: 17075.4]
[Processed Buffers: Min: 739, Avg: 831.5, Max: 879, Diff: 140, Sum: 13304]
[Scan RS (ms): Min: 3610.8, Avg: 3611.6, Max: 3612.6, Diff: 1.8, Sum: 57786.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.3, Diff: 0.3, Sum: 0.5]
[Object Copy (ms): Min: 545.2, Avg: 546.3, Max: 547.2, Diff: 2.0, Sum: 8741.5]
[Termination (ms): Min: 0.1, Avg: 0.6, Max: 0.8, Diff: 0.7, Sum: 9.8]
[Termination Attempts: Min: 1, Avg: 465.3, Max: 552, Diff: 551, Sum: 7445]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.4]
[GC Worker Total (ms): Min: 5227.3, Avg: 5227.4, Max: 5227.5, Diff: 0.2, Sum: 83638.8]
[GC Worker End (ms): Min: 365573811.8, Avg: 365573811.8, Max: 365573811.8, Diff: 0.0]
[Code Root Fixup: 0.4 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 3.4 ms]
[Other: 68.4 ms]
[Choose CSet: 16.3 ms]
[Ref Proc: 3.6 ms]
[Ref Enq: 0.2 ms]
[Redirty Cards: 8.7 ms]
[Humongous Register: 0.1 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 31.6 ms]
[Eden: 3936.0M(3936.0M)->0.0B(3904.0M) Survivors: 160.0M->192.0M Heap: 46.5G(80.0G)->42.3G(80.0G)]
Heap after GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 44323410K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 6 young (196608K), 6 survivors (196608K)
Metaspace used 50945K, capacity 51410K, committed 52168K, reserved 53248K
}
[Times: user=85.30 sys=0.00, real=5.30 secs]
2019-02-18T17:48:58.871+0800: 365573.867: Total time for which application threads were stopped: 5.3131110 seconds, Stopping threads took: 0.0005771 seconds
2019-02-18T17:48:58.896+0800: 365573.893: Total time for which application threads were stopped: 0.0186168 seconds, Stopping threads took: 0.0005541 seconds
2019-02-18T17:48:58.906+0800: 365573.902: Total time for which application threads were stopped: 0.0078098 seconds, Stopping threads took: 0.0005988 seconds
2019-02-18T17:48:58.915+0800: 365573.912: Total time for which application threads were stopped: 0.0075271 seconds, Stopping threads took: 0.0003996 seconds
2019-02-18T17:48:58.923+0800: 365573.919: Total time for which application threads were stopped: 0.0072014 seconds, Stopping threads took: 0.0003114 seconds
2019-02-18T17:48:58.934+0800: 365573.930: Total time for which application threads were stopped: 0.0078706 seconds, Stopping threads took: 0.0005490 seconds
2019-02-18T17:48:58.942+0800: 365573.938: Total time for which application threads were stopped: 0.0073198 seconds, Stopping threads took: 0.0003927 seconds
2019-02-18T17:48:58.964+0800: 365573.960: Total time for which application threads were stopped: 0.0078810 seconds, Stopping threads took: 0.0007641 seconds
2019-02-18T17:48:58.972+0800: 365573.969: Total time for which application threads were stopped: 0.0075881 seconds, Stopping threads took: 0.0006277 seconds
2019-02-18T17:48:58.981+0800: 365573.978: Total time for which application threads were stopped: 0.0081290 seconds, Stopping threads took: 0.0011246 seconds
2019-02-18T17:48:58.992+0800: 365573.989: Total time for which application threads were stopped: 0.0076556 seconds, Stopping threads took: 0.0005358 seconds
2019-02-18T17:48:59.015+0800: 365574.011: Total time for which application threads were stopped: 0.0076750 seconds, Stopping threads took: 0.0005602 seconds
2019-02-18T17:48:59.026+0800: 365574.022: Total time for which application threads were stopped: 0.0089086 seconds, Stopping threads took: 0.0006000 seconds
2019-02-18T17:48:59.044+0800: 365574.041: Total time for which application threads were stopped: 0.0087554 seconds, Stopping threads took: 0.0006332 seconds
2019-02-18T17:48:59.054+0800: 365574.050: Total time for which application threads were stopped: 0.0084038 seconds, Stopping threads took: 0.0004326 seconds
{Heap before GC invocations=50104 (full 0):
garbage-first heap total 83886080K, used 48321106K [0x00007faec4000000, 0x00007faec6005000, 0x00007fc2c4000000)
region size 32768K, 128 young (4194304K), 6 survivors (196608K)
Metaspace used 50946K, capacity 51410K, committed 52168K, reserved 53248K

从日志看,期望survivor区有大概256MB,实际只有32*5=160MB,可能会将eden区直接提到old区。

近期gc时间占比过高,到40%了。

清理young 区的gc线程耗时过长,清理后可回收内存低于10%,mixed gc结束,其他gc线程空闲。说明mixed gc触发阈值设置低了,young gc参数也需要调整。

看暂停时间大于2秒的其他日志,都是触发mixed gc,但只有一个清理young和个别old区region的线程工作。确实说明mixed gc触发阈值设置低了,young gc过大了。
 
 


有41次gc的系统时间大于用户时间,大概3小时一次。

操作系统层面有可能是操作系统层异常,或者系统太忙了。出现频率大概3小时一次,看ganglia日志问题出现时cpu负载并不高。并且系统时间只是略高于用户时间不是明显有问题的数量级差距。先不考虑操作系统和JVM有bug的情况,暂不处理。

 

还是说有5秒的gc,同上。


修改参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+UnlockExperimentalVMOptions
-XX:+ParallelRefProcEnabled
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=16
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=20
-XX:MaxTenuringThreshold=1
-XX:G1HeapRegionSize=32m
-XX:G1MixedGCCountTarget=16
-XX:InitiatingHeapOccupancyPercent=60
-XX:MaxDirectMemorySize=110g
-XX:G1OldCSetRegionThresholdPercent=10
-XX:G1HeapWastePercent=10
-Xmx80g
-Xms80g


从gc前后堆内存图来看,-XX:InitiatingHeapOccupancyPercent=65,52GB更合适。

暂停时间平均135ms,从暂停时间图来看,young gc时间较长,需要调小young gc区,小米经验改初始值,由5改成2。-XX:G1NewSizePercent=2

mixed gc 大多数一轮不到8次,暂停时间基本都超过100ms了。期待每轮16次的参数没起作用,这是由于每轮清理的region数过多。-XX:G1OldCSetRegionThresholdPercent=5,由10改成5,减少单次mixed gc时间。

survivor区应该大于young日志中期待的,Set SurvivorRatio from 8 (default) to 4。
-XX:InitiatingHeapOccupancyPercent=65
-XX:G1NewSizePercent=2
-XX:G1OldCSetRegionThresholdPercent=5
-XX:SurvivorRatio=4



设置其他必要参数

参考各类优化资料,增加以下参数
-XX:+UseStringDeduplication 字符串去重,提高性能
-XX:-ResizePLAB 减少gc线程间通信的东西,关闭动态提升本地buffer
-XX:+PerfDisableSharedMem 关掉统计信息的内存映射。开启在某些特殊场景下,会极大增加gc暂停时间

优化结果




平均暂停时间,最大暂停时间,暂停时间分布,gc吞吐占比都有提高,优化成功。

接下来迭代提高这台服务器region数,做gc优化。

 
遗留问题

偶发系统时间大于用户时间的问题,依然存在。发生时系统负载并不高,有可能操作系统和JVM层面有优化空间。

gceasy建议不要用-XX:+UseGCLogFileRotation,理由是历史会看不到,文件顺序不容易分辨,多个文件不利于gc分析。但这个参数意义是控制总文件大小,所以依然保留。

附录
g1官方介绍


https://www.oracle.com/technet ... .html

其他可视化工具介绍

https://blog.csdn.net/qq_32447 ... 67984

https://blog.csdn.net/xuelinme ... 90115

https://blog.csdn.net/renfufei ... 78064

gceasy优化建议

https://blog.gceasy.io/2016/11 ... uses/

https://blog.gceasy.io/2016/12 ... time/

https://blog.gceasy.io/2016/12 ... pped/

https://blog.gceasy.io/2018/12 ... tion/

https://blog.gceasy.io/2019/01 ... tion/

优化参考

https://www.oracle.com/technet ... .html

https://blogs.apache.org/hbase ... hbase

https://software.intel.com/en- ... hbase

http://openinx.github.io/ppt/h ... 8.pdf

https://blog.csdn.net/songhaif ... 77612

https://blog.csdn.net/maosijun ... 62489

https://dzone.com/articles/g1g ... ags-1

http://www.evanjones.ca/jvm-mmap-pause.html

之前的优化探索
 http://www.hbase.group/article/192
 
专栏地址
 
https://zhuanlan.zhihu.com/c_178811296

HBase社区福利 | HBase初学者的福利来袭

hbase过往记忆 发表了文章 • 0 个评论 • 498 次浏览 • 2019-02-20 13:46 • 来自相关话题

HBase 是一个分布式存储、数据库引擎,可以支持千万的QPS、PB级别的存储,这些都已经在生产环境验证,并且在广大的公司有大量的应用。根据目前公开的数据,阿里巴巴 HBase 集群规模为 12000 多台;京东 HBase 集群规模为 5000 多台;360 HBase 规模为 12500 台;爱奇艺 6000 多台。

HBase 可以说是一个数据库,也可以说是一个存储。拥有双重属性的 HBase 天生就具备广阔的应用场景。在2.0中,引入了OffHeap降低了延迟,可以满足在线的需求。引入MOB,可以存储10M左右的对象,完全适应了对象存储。另外由于自身的并发能力、存储能力,可以说是一个很有竞争力的引擎。HBase 在各大公司的应用场景包括以下几点:
 
推荐画像:特别是用户的画像,是一个比较大的稀疏矩阵,蚂蚁的风控就是构建在HBase之上对象存储:我们知道不少的头条类、新闻类的的新闻、网页、图片存储在HBase之中,一些病毒公司的病毒库也是存储在HBase之中时序数据:HBase之上有OpenTSDB模块,可以满足时序类场景的需求时空数据:主要是轨迹、气象网格之类,滴滴打车的轨迹数据主要存在HBase之中,另外在技术所有大一点的数据量的车联网企业,数据都是存在HBase之中CubeDB OLAP:Kylin一个cube分析工具,底层的数据就是存储在HBase之中,不少客户自己基于离线计算构建cube存储在hbase之中,满足在线报表查询的需求消息/订单:在电信领域、银行领域,不少的订单查询底层的存储,另外不少通信、消息同步的应用构建在HBase之上Feeds流:典型的应用就是xx朋友圈类似的应用NewSQL:之上有Phoenix的插件,可以满足二级索引、SQL的需求,对接传统数据需要SQL非事务的需求。

HBase 有着如此多的应用,越来越多的公司都在使用它,这就导致越来越多的开发者们需要学习 HBase。但是HBase是一种分布式的数据库,其正常运行需要部署 HDFS、Zookeeper以及HBase等相关组件;这对于一个新手来说特别复杂,间接增加了我们的学习成本。其实对大部分用户来说,只是想简单的使用 HBase,比如使用它来读写数据,验证某些场景的可行性。对这部分用户,其实他们并不想花太多时间在部署各种组件上面。

基于这些原因,中国HBase技术社区联合阿里云HBase数据库团队为大家带来了福利。阿里云HBase为我们提供了免费的HBase环境,如下:




我们可以通过 https://promotion.aliyun.com/ntms/act/hbasefree.html 链接去申请入。 查看全部
HBase 是一个分布式存储、数据库引擎,可以支持千万的QPS、PB级别的存储,这些都已经在生产环境验证,并且在广大的公司有大量的应用。根据目前公开的数据,阿里巴巴 HBase 集群规模为 12000 多台;京东 HBase 集群规模为 5000 多台;360 HBase 规模为 12500 台;爱奇艺 6000 多台。

HBase 可以说是一个数据库,也可以说是一个存储。拥有双重属性的 HBase 天生就具备广阔的应用场景。在2.0中,引入了OffHeap降低了延迟,可以满足在线的需求。引入MOB,可以存储10M左右的对象,完全适应了对象存储。另外由于自身的并发能力、存储能力,可以说是一个很有竞争力的引擎。HBase 在各大公司的应用场景包括以下几点:
 
  • 推荐画像:特别是用户的画像,是一个比较大的稀疏矩阵,蚂蚁的风控就是构建在HBase之上
  • 对象存储:我们知道不少的头条类、新闻类的的新闻、网页、图片存储在HBase之中,一些病毒公司的病毒库也是存储在HBase之中
  • 时序数据:HBase之上有OpenTSDB模块,可以满足时序类场景的需求
  • 时空数据:主要是轨迹、气象网格之类,滴滴打车的轨迹数据主要存在HBase之中,另外在技术所有大一点的数据量的车联网企业,数据都是存在HBase之中
  • CubeDB OLAP:Kylin一个cube分析工具,底层的数据就是存储在HBase之中,不少客户自己基于离线计算构建cube存储在hbase之中,满足在线报表查询的需求
  • 消息/订单:在电信领域、银行领域,不少的订单查询底层的存储,另外不少通信、消息同步的应用构建在HBase之上
  • Feeds流:典型的应用就是xx朋友圈类似的应用
  • NewSQL:之上有Phoenix的插件,可以满足二级索引、SQL的需求,对接传统数据需要SQL非事务的需求。


HBase 有着如此多的应用,越来越多的公司都在使用它,这就导致越来越多的开发者们需要学习 HBase。但是HBase是一种分布式的数据库,其正常运行需要部署 HDFS、Zookeeper以及HBase等相关组件;这对于一个新手来说特别复杂,间接增加了我们的学习成本。其实对大部分用户来说,只是想简单的使用 HBase,比如使用它来读写数据,验证某些场景的可行性。对这部分用户,其实他们并不想花太多时间在部署各种组件上面。

基于这些原因,中国HBase技术社区联合阿里云HBase数据库团队为大家带来了福利。阿里云HBase为我们提供了免费的HBase环境,如下:
微信截图_20190220134510.png

我们可以通过 https://promotion.aliyun.com/ntms/act/hbasefree.html 链接去申请入。

2019 HBase Meetup 演讲者和议题征集

HBase Meetup过往记忆 发表了文章 • 0 个评论 • 824 次浏览 • 2019-02-14 11:00 • 来自相关话题

HBase Meetup 会议由 HBase技术社区主办,在全国各大城市举办。在过去的2018年,我们在北京、上海、杭州、深圳以及武汉等城市举办了9场 HBase Meetup 会议,来自各大公司的 HBase PMC、committer 以及 HBase 开发者共聚一堂,为大家分享了 HBase 技术解析及应用实践。 
 
2019年,我们继续在全国举办 HBase 线下交流会。现向大家征集这几次会议的大会演讲者和议题,如果大家有意来分享,可以到 http://hbasegroup.mikecrm.com/zh19LHN 参加调查问卷,欢迎大家踊跃参与。同时愿意主动主办的公司,也可以来联系我。
 
议题范围:HBase、Spark、Phoenix、Solr、时序、时空以及图等使用案例,相关经验等。
投送议题时需要说明议题名称和简介,PPT可后续完成。
 
报名咨询:
微信:iteblog (备注 HBase Meetup)
钉钉:rvix4rb

各地 Meetup 举办时间:
  查看全部
HBase Meetup 会议由 HBase技术社区主办,在全国各大城市举办。在过去的2018年,我们在北京、上海、杭州、深圳以及武汉等城市举办了9场 HBase Meetup 会议,来自各大公司的 HBase PMC、committer 以及 HBase 开发者共聚一堂,为大家分享了 HBase 技术解析及应用实践。 
 
2019年,我们继续在全国举办 HBase 线下交流会。现向大家征集这几次会议的大会演讲者和议题,如果大家有意来分享,可以到 http://hbasegroup.mikecrm.com/zh19LHN 参加调查问卷,欢迎大家踊跃参与。同时愿意主动主办的公司,也可以来联系我。
 
议题范围:HBase、Spark、Phoenix、Solr、时序、时空以及图等使用案例,相关经验等。
投送议题时需要说明议题名称和简介,PPT可后续完成。
 
报名咨询:
微信:iteblog (备注 HBase Meetup)
钉钉:rvix4rb

各地 Meetup 举办时间:
 

2018 HBase技术总结

hbasehbasegroup 发表了文章 • 0 个评论 • 3147 次浏览 • 2019-01-05 13:02 • 来自相关话题

HBase 是一个高性能,并且支持无限水平扩展的在线数据库,其存储计算分离的特性非常好地适应了目前的趋势,并且在国内大公司内都被广泛地应用,具有非常好的生态,是构建大数据系统的不二选择。
 
HBase 是一个高性能,并且支持无限水平扩展的在线数据库,其存储计算分离的特性非常好地适应了目前的趋势,并且在国内大公司内都被广泛地应用,具有非常好的生态,是构建大数据系统的不二选择。
 

2018年HBase生态社群画像 +最全资料汇总下载

hbase过往记忆 发表了文章 • 0 个评论 • 606 次浏览 • 2019-01-02 16:39 • 来自相关话题

钉群直播全部资料下载:下载
 








9届Meetup视频和PPT下载:下载






《58HBase平台实践和应用 -平台建设篇》
何良均/张祥 58同城
查看
《HBase Rowkey 设计指南》
吴阳平 阿里巴巴HBase业务架 构师
查看
《HBase 2.0之修复工具HBCK2 运维指南》
田竞云 小米HBase Committer
查看
《从NoSQL到NewSQL,凤 凰涅槃成就Phoenix》
张赟 阿里巴巴 Phoenix Committer
查看
《消灭毛刺!HBase2.0全链路offheap效果拔群》
杨文龙 阿里巴巴技术专家 HBase Committer&HBase PMC
查看
《解读HBase2.0新功能 AssignmentManagerV2》
杨文龙 阿里巴巴技术专家 HBase Committer&Hbase PMC
查看
《深入解读HBase2.0新功能 高可用读Region Replica》
杨文龙 阿里巴巴技术专家 HBase Committer&Hbase PMC
查看
《HBase最佳实践-读性能 优化策略》
范欣欣 网易 HBase 资深开发工程师
查看
《HBase2.0新特性之In- Memory Compaction》
陆豪 阿里巴巴技术专家
查看
《HBase Coprocessor的实现与应用》
叶铿 烽火 大数据平台负责人
查看
《HBase实战之MOB使用指南》
查看
《HBase在新能源汽车监控系统中的应用》
颜禹 重庆博尼施CTO
查看
《HBase在爱奇艺的应用实践》
郑昊南 爱奇艺 资深研发工程师
查看
《HBase在滴滴出行的应用场景和最佳实践》
李扬 滴滴
查看
《HBase在人工智能场景的使用》
吴阳平 阿里HBase业务架构师
查看
《车纷享:基于阿里云 HBase构建车联网平台实践》
查看
《HBase基本知识介绍及典型 案例分析》
吴阳平 阿里HBase业务架构师
查看























HBase生态+Spark社区 云栖号:https://yq.aliyun.com/teams/382
中国 HBase 技术社区网站:http://hbase.group 查看全部
d31742f41fe14a640fcc4b3b527f04985e00dd2c.png

f77a3002db4fae240559d8cc3fe8b92c64ed4937.png

26e49202a039fe1e3eb1adf6470f07d87716769f.png

40cc47db9c4e7f674843066f3ec2633979d75a72.png

388942cd244f470b6f655d62d3f576757dbff8af.png

a65ddc521b8fadf40f57094b44a917a9179ef60f.png

0f1570d0d4240064e6de0815d4441e0c61d988b5.png

27d414dbdcbb2d680461513a5b255b417b3fc70c.png

钉群直播全部资料下载:下载
 
e774dbc14e69ff6895ec38b329b89a30650882ec.png

2200687ad1a586fd54358ae539511f8c0ea8c15c.png

9届Meetup视频和PPT下载:下载

0ae287f151e65e060e273905f916f76805e6f5b4.png


《58HBase平台实践和应用 -平台建设篇》
何良均/张祥 58同城
查看
《HBase Rowkey 设计指南》
吴阳平 阿里巴巴HBase业务架 构师
查看
《HBase 2.0之修复工具HBCK2 运维指南》
田竞云 小米HBase Committer
查看
《从NoSQL到NewSQL,凤 凰涅槃成就Phoenix》
张赟 阿里巴巴 Phoenix Committer
查看
《消灭毛刺!HBase2.0全链路offheap效果拔群》
杨文龙 阿里巴巴技术专家 HBase Committer&HBase PMC
查看
《解读HBase2.0新功能 AssignmentManagerV2》
杨文龙 阿里巴巴技术专家 HBase Committer&Hbase PMC
查看
《深入解读HBase2.0新功能 高可用读Region Replica》
杨文龙 阿里巴巴技术专家 HBase Committer&Hbase PMC
查看
《HBase最佳实践-读性能 优化策略》
范欣欣 网易 HBase 资深开发工程师
查看
《HBase2.0新特性之In- Memory Compaction》
陆豪 阿里巴巴技术专家
查看
《HBase Coprocessor的实现与应用》
叶铿 烽火 大数据平台负责人
查看
《HBase实战之MOB使用指南》
查看
《HBase在新能源汽车监控系统中的应用》
颜禹 重庆博尼施CTO
查看
《HBase在爱奇艺的应用实践》
郑昊南 爱奇艺 资深研发工程师
查看
《HBase在滴滴出行的应用场景和最佳实践》
李扬 滴滴
查看
《HBase在人工智能场景的使用》
吴阳平 阿里HBase业务架构师
查看
《车纷享:基于阿里云 HBase构建车联网平台实践》
查看
《HBase基本知识介绍及典型 案例分析》
吴阳平 阿里HBase业务架构师
查看

9daceab1cd0cfec37d5122c10d424251cddaea94.png

89d798bac2565ae1ea89845a5f2b34f2b4555443.png

4a5346ef14879ecd66262f0f6bfbf9d178a1e6da.png

5a613c85a5a65c4777f339ad0a2a6700c3cced98.png


5d6d269caa4deb5ea85092c36ac5aa461ae8d6b2.png


HBase生态+Spark社区 云栖号:https://yq.aliyun.com/teams/382
中国 HBase 技术社区网站:http://hbase.group

HBase在58的实践和应用

HBase Meetup过往记忆 发表了文章 • 0 个评论 • 800 次浏览 • 2018-12-29 09:28 • 来自相关话题

本文是58何良均嘉宾在中国HBase技术社区第九届meetup-HBase典型应用场景与实践的分享。介绍内容:HBase在58的实践和应用,包括平台建设、生态建设、平台监控、平台运营等
 
本文是58何良均嘉宾在中国HBase技术社区第九届meetup-HBase典型应用场景与实践的分享。介绍内容:HBase在58的实践和应用,包括平台建设、生态建设、平台监控、平台运营等
 

中国HBase技术社区第九届meetup-HBase典型应用场景与实践

HBase Meetup过往记忆 发表了文章 • 0 个评论 • 373 次浏览 • 2018-12-18 19:38 • 来自相关话题

会议时间
2018年12月23日 14:00 ~ 2018年12月23日 18:00  (北京朝阳)360公司 A座一层发布厅,报名地址:http://www.huodongxing.com/event/1469391290300





 本期嘉宾介绍





本期活动主题

  13:30-14:00  

   签到

  14:00-14:40 

  HBase 2.0 在360的技术改进与应用实践       

  讲师:王小勇——360系统部分布式存储方向架构师

  介绍内容:HBase在360的使用现状和发展历程,以及在升级HBase2.0的过程中发现的问题与改进。

  14:40-15:20   

   HBase 基本知识介绍及典型案例分析

   讲师:吴阳平——阿里云HBase业务架构师

   介绍内容:HBase基础知识介绍,Rowkey设计技巧,HBase企业级特性及组件介绍,HBase+Spark典型案例分析。

  15:20-15:30   

   抽奖环节 送360、阿里、京东、58专属定制的礼品

  15:30-16:10    

   HBase在无界零售中的应用

   讲师:诸葛子房——京东大数据工程师

   介绍内容:Hbase存储的优势;Hbase案例分享;Hbase数据分析

  16:10-16:50    

   图数据库hgraphdb介绍

   讲师:陈江——阿里高级技术专家

   介绍内容:HGraphDB 是一个使用 HBase 作为底层存储的图数据库, 是 Apache TinkerPop 3 接口的实现。


  16:50-17:30   

   HBase在58的实践和应用

   讲师:何良均——58大数据工程师

   介绍内容:HBase在58的实践和应用,包括平台建设、生态建设、平台监控、平台运营等


主办:中国HBase技术社区

协办:360技术委员会;阿里云飞天八部多模型数据库组;云栖社区;360大学;360系统部,DataFun社区

合作伙伴:开源中国;SegmentFault;掘金;示说网

活动官方报名平台:活动行


技术社群及公众号推荐:


【HBase中国钉钉技术交流社群】:

为了更好的服务HBase开发者,我们启用了钉钉企业号。并每周在群内进行【技术分享直播】和【在线回答技术问题】。

欢迎大家入群,点击link 入群:  https://dwz.cn/Fvqv066s 查看全部
会议时间
2018年12月23日 14:00 ~ 2018年12月23日 18:00  (北京朝阳)360公司 A座一层发布厅,报名地址:http://www.huodongxing.com/event/1469391290300

head_image.jpg

 本期嘉宾介绍

微信图片_20181218193751.jpg

本期活动主题

  13:30-14:00  

   签到

  14:00-14:40 

  HBase 2.0 在360的技术改进与应用实践       

  讲师:王小勇——360系统部分布式存储方向架构师

  介绍内容:HBase在360的使用现状和发展历程,以及在升级HBase2.0的过程中发现的问题与改进。

  14:40-15:20   

   HBase 基本知识介绍及典型案例分析

   讲师:吴阳平——阿里云HBase业务架构师

   介绍内容:HBase基础知识介绍,Rowkey设计技巧,HBase企业级特性及组件介绍,HBase+Spark典型案例分析。

  15:20-15:30   

   抽奖环节 送360、阿里、京东、58专属定制的礼品

  15:30-16:10    

   HBase在无界零售中的应用

   讲师:诸葛子房——京东大数据工程师

   介绍内容:Hbase存储的优势;Hbase案例分享;Hbase数据分析

  16:10-16:50    

   图数据库hgraphdb介绍

   讲师:陈江——阿里高级技术专家

   介绍内容:HGraphDB 是一个使用 HBase 作为底层存储的图数据库, 是 Apache TinkerPop 3 接口的实现。


  16:50-17:30   

   HBase在58的实践和应用

   讲师:何良均——58大数据工程师

   介绍内容:HBase在58的实践和应用,包括平台建设、生态建设、平台监控、平台运营等


主办:中国HBase技术社区

协办:360技术委员会;阿里云飞天八部多模型数据库组;云栖社区;360大学;360系统部,DataFun社区

合作伙伴:开源中国;SegmentFault;掘金;示说网

活动官方报名平台:活动行


技术社群及公众号推荐:


【HBase中国钉钉技术交流社群】:

为了更好的服务HBase开发者,我们启用了钉钉企业号。并每周在群内进行【技术分享直播】和【在线回答技术问题】。

欢迎大家入群,点击link 入群:  https://dwz.cn/Fvqv066s

使用 Spark 分析 HBase 的数据

hbase过往记忆 发表了文章 • 2 个评论 • 872 次浏览 • 2018-11-28 11:08 • 来自相关话题

HBase具有很好的在线入库和查询能力,不过在分析上面有比较大的欠缺,这篇文章主要介绍如何使用Spark对HBase中数据做复杂分析。

HBase查询分析的现状
 
HBase原生API:HBase原生API适合基于row key做点查,这个是HBase最擅长的查询场景Phoenix:Phoenix作为HBase的SQL层,使用二级索引的技术,擅长多条件组合查询;Phoenix没有自己的计算资源,类似groupby这样的复杂查询需要借助HBase的协处理器来完成,这样一方面性能不好,同时会影响HBase集群的稳定性;Spark:具有丰富的算子支持复杂分析,使用Spark集群的计算资源,通过并发分析的方式可以提高性能,同时不影响HBase集群的稳定性。

Spark分析HBase的方式对比

Spark分析HBase数据有“RDD API”、“SQL API”、“HFILE”三种方式,相关对比如下:





 
对于数据动态更新增加的小表推荐使用SQL API的方式,可以有效的优化分析,减少对HBase集群稳定性的影响;对于静态表或者全量静态表的分析推荐使用分析HFILE的方式直读HDFS,这样可以完全不影响HBase集群稳定性;不推荐使用RDD API 的方式,这种方式一方没有优化性能差,同时在高并发以及表数据量大时,会严重影响HBase集群的稳定性,从而影响在线业务。

 三种方式的具体使用

阿里云HBase团队为大家提供了一个github项目供大家参考使用上面的三种方式来开发Spark分析HBase的程序,项目地址:

https://github.com/lw309637554/alicloud-hbase-spark-examples

依赖项:需要下载云HBase及云Phoenix的client包
分析HFILE:
需要先开通云HBase的HDFS访问权限,参考文档在hbase shell中对表生成snapshot表“snapshot 'sourceTable', ‘snapshotName'”在项目中配置自己的hdfs-sit.xml文件,然后通过直读HDFS的方式分析snapshot表

具体的example
RDD API对应:org.apache.spark.hbase.NativeRDDAnalyzeSQL API对应:org.apache.spark.sql.execution.datasources.hbase.SqlAnalyze分析HFILE对应:org.apache.spark.hfile.SparkAnalyzeHFILE
  查看全部
HBase具有很好的在线入库和查询能力,不过在分析上面有比较大的欠缺,这篇文章主要介绍如何使用Spark对HBase中数据做复杂分析。

HBase查询分析的现状
 
  • HBase原生API:HBase原生API适合基于row key做点查,这个是HBase最擅长的查询场景
  • Phoenix:Phoenix作为HBase的SQL层,使用二级索引的技术,擅长多条件组合查询;Phoenix没有自己的计算资源,类似groupby这样的复杂查询需要借助HBase的协处理器来完成,这样一方面性能不好,同时会影响HBase集群的稳定性;
  • Spark:具有丰富的算子支持复杂分析,使用Spark集群的计算资源,通过并发分析的方式可以提高性能,同时不影响HBase集群的稳定性。


Spark分析HBase的方式对比

Spark分析HBase数据有“RDD API”、“SQL API”、“HFILE”三种方式,相关对比如下:

menu.saveimg_.savepath20181128110650_.jpg

 
对于数据动态更新增加的小表推荐使用SQL API的方式,可以有效的优化分析,减少对HBase集群稳定性的影响;对于静态表或者全量静态表的分析推荐使用分析HFILE的方式直读HDFS,这样可以完全不影响HBase集群稳定性;不推荐使用RDD API 的方式,这种方式一方没有优化性能差,同时在高并发以及表数据量大时,会严重影响HBase集群的稳定性,从而影响在线业务。

 三种方式的具体使用

阿里云HBase团队为大家提供了一个github项目供大家参考使用上面的三种方式来开发Spark分析HBase的程序,项目地址:

https://github.com/lw309637554/alicloud-hbase-spark-examples

依赖项:需要下载云HBase及云Phoenix的client包
分析HFILE:
  • 需要先开通云HBase的HDFS访问权限,参考文档
  • 在hbase shell中对表生成snapshot表“snapshot 'sourceTable', ‘snapshotName'”
  • 在项目中配置自己的hdfs-sit.xml文件,然后通过直读HDFS的方式分析snapshot表


具体的example
  • RDD API对应:org.apache.spark.hbase.NativeRDDAnalyze
  • SQL API对应:org.apache.spark.sql.execution.datasources.hbase.SqlAnalyze
  • 分析HFILE对应:org.apache.spark.hfile.SparkAnalyzeHFILE

 

HBase 生态(多模式)

hbase过往记忆 发表了文章 • 0 个评论 • 1130 次浏览 • 2018-11-07 15:18 • 来自相关话题

经过近十年的发展,目前 HBase 已经支持了许多组件用于解决不同场景的问题,这组件包括:
Phoenix : SQL on HBaseSpark : OLAP、Streaming、SQLSolr:二级索引、全文索引OpenTSDB:时序HGraphDB:图GeoMesa:时空 查看全部
经过近十年的发展,目前 HBase 已经支持了许多组件用于解决不同场景的问题,这组件包括:
  1. Phoenix : SQL on HBase
  2. Spark : OLAP、Streaming、SQL
  3. Solr:二级索引、全文索引
  4. OpenTSDB:时序
  5. HGraphDB:图
  6. GeoMesa:时空

HBase Region Balance实践

hbase过往记忆 发表了文章 • 0 个评论 • 558 次浏览 • 2018-10-30 17:34 • 来自相关话题

HBase是一种支持自动负载均衡的分布式KV数据库,在开启balance的开关(balance_switch)后,HBase的HMaster进程会自动根据 指定策略 挑选出一些Region,并将这些Region分配给负载比较低的RegionServer上。官方目前支持两种挑选Region的策略,一种叫做DefaultLoadBalancer,另一种叫做StochasticLoadBalancer,这两种策略后面会具体讲到。由于HBase的所有数据(包括HLog/Meta/HStoreFile等)都是写入到HDFS文件系统中的, 因此HBase的Region移动其实非常轻量级。在做Region移动的时候,保持这个Region对应的HDFS文件位置不变,只需要将Region的Meta数据分配到相关的RegionServer即可,整个Region移动的过程取决于RegionClose以及RegionOpen的耗时,这个时间一般都很短。
本文来讲讲hbase的balance实现。balance的流程
首先通过LoadBalancer找出所有需要移动的region plan,一个region plan包括region/原始RegionServer/目的RegionServer三个属性。unassign region , 将region从原来的RegionServer上解除绑定;assign region ,将region绑定到目标RegionServer上;
其中, unassign region的具体流程为:
create zk closing node . 该节点在/unassigned路径下, 包含(znode状态,region名字,原始RS名,payload)这些数据。hmaster 调用rpc服务关闭region server。region-close的流程大致为先获取region的writeLock , 然后flush memstore, 再并发关闭该region下的所有的store file文件(注意一个region有多个store,每个store又有多个store file , 所以可以实现并发close store file) 。最后释放region的writeLock.设置zk closing node的znode状态为closed.
assgin region的具体流程为:
获取到对应的Region Plan.HMaster调用rpc服务去Region Plan对应的RegionServer上open region. 这里会先更新/unassigned节点为opening. 然后并发Load HStore,再更行zk/ROOT/META表信息,这里是为了client下次能获取到正确的路由信息, 最后更新region状态为OPEN.
DefaultLoadBalancer策略
这种策略能够保证每个RS的regions个数基本上都相等,确切来说,假设一共有n个RS,第i个RS有Ai个region,记average=sigma(Ai)/n , 那么这种策略能够保证所有的RS的region个数都在[floor(average), ceil(average)]之间。这种策略的实现简单,应用广泛。
但是,这种策略考虑的因素比较单一, 没有考虑到每台region server的读写qps/负载压力等等,这样就可能导致出现一种情况:虽然每个region server的regions都非常接近,但是90%的请求还是落在了一台RS上,因为这台RS上的region全部都是热点数据,这样还是没有达到负载均衡的目的。 但我觉得balance的首要目的是保证数据均衡,如果在数据均衡的情况下,负载还是集中,这时候就要考虑下rowKey的选择是否有问题了。因此, 我个人还是比较推荐采用DefaultLoadBalancer的。StochasticLoadBalancer策略
StochasticLoadBalancer 这种策略真的是非常复杂,简单来讲,是一种综合权衡一下6个因素的均衡策略:
每台RegionServer读请求数(ReadRequestCostFunction)每台RegionServer写请求数(WriteRequestCostFunction)每台RegionServer的Region个数(RegionCountSkewCostFunction)移动代价(MoveCostFunction)数据locality(TableSkewCostFunction)每张表占据RegionServer中region个数上限(LocalityCostFunction)
对于cluster的每一种region分布, 采用6个因素加权的方式算出一个代价值,这个代价值就用来评估当前region分布是否均衡,越均衡则代价值越低。然后通过成千上万次随机迭代来找到一组RegionMove的序列,使得最终的代价值严格递减。 得到的这一组RegionMove就是HMaster最终执行的region迁移方案。
这里用一段伪代码来描述这个迭代的过程:
[code]currentCost = MAX ;
plans = []
for(step = 0 ; step < 1000000; step ++ ){
action = cluster.generateMove()
doAction( action );
newCost = computeCost(action) ;
if (newCost < currentCost){
currentCost = newCost;
plans.add( action );
}else{
undoAction(action);
}
}
[/code]
其中generateMove()每次随机选择以下3种策略中的一种来生成RegionMove:
随机选择两个RS, 从每个RS中随机选择两个Region,然后生成一个Action, 这个Action有一半概率做RegionMove(从Region多的RS迁移到Region少的RS), 另一半概率做RegionSwap(两个RS之间做Region的交换)。选择Region最多的RS和Region最少的RS,然后生成一个Action, 这个Action一半概率做RegionMove, 一半概率做RegionSwap。随机找一个RS,然后找到该RS上数据locality最差的Region,再找到Region大部分数据落在的RS,然后生成一个Action,该Action用来把Region迁移到它应该所在的RS,用来提高locality.
对于这种策略,JavaDoc上说效果比较好,但其中的合理性个人觉得有待测试数据的证明(官方基本没有给出这方面的测试结果)。如果6个因素每个参数占据的权重如果没有调好的话,会导致线上的Region大量不均衡。按照我的一次线上经历,采用如下blance配置,出现过每次balance都只选择60个左右的plan去移动, 但真实的情况是145个RS,其中region数量最多的有700+个, 最少的region数量有2个,然后其他RS的region数量在2~700不等,这时候按理来讲应该需要进行大量的balance,但HMaster每隔一个period只生成60个plan左右去移动,这样balance太慢导致很长一段时间内负载不均,有的RS非常清闲,有的RS非常繁忙经常超时。
[code]hbase.master.loadbalancer.class=\
org.apache.hadoop.hbase.master.StochasticLoadBalancer
hbase.master.balancer.stochastic.regionCountCost=10
hbase.master.balancer.stochastic.tableSkewCost=5
hbase.master.balancer.stochastic.readRequestCost=5
hbase.master.balancer.stochastic.writeRequestCost=5
hbase.master.balancer.stochastic.localityCost=10
hbase.master.balancer.stochastic.moveCost=4
hbase.master.balancer.stochastic.maxMovePercent=1
[/code]
后面对比了下了官方的默认配置,应该是regionCountCost一项权重太低, 但是,我想说的是除非线下有一个测试结果支撑具体的权重配置下 balance是符合预期的,否则线上操作时一般对权重很难有一个准确的把握,所以像这么复杂的策略还是要比较谨慎的选择,最好有过历史测试数据来评估balance的效果。 查看全部
HBase是一种支持自动负载均衡的分布式KV数据库,在开启balance的开关(balance_switch)后,HBase的HMaster进程会自动根据 指定策略 挑选出一些Region,并将这些Region分配给负载比较低的RegionServer上。官方目前支持两种挑选Region的策略,一种叫做DefaultLoadBalancer,另一种叫做StochasticLoadBalancer,这两种策略后面会具体讲到。由于HBase的所有数据(包括HLog/Meta/HStoreFile等)都是写入到HDFS文件系统中的, 因此HBase的Region移动其实非常轻量级。在做Region移动的时候,保持这个Region对应的HDFS文件位置不变,只需要将Region的Meta数据分配到相关的RegionServer即可,整个Region移动的过程取决于RegionClose以及RegionOpen的耗时,这个时间一般都很短。
本文来讲讲hbase的balance实现。balance的流程
  • 首先通过LoadBalancer找出所有需要移动的region plan,一个region plan包括region/原始RegionServer/目的RegionServer三个属性。
  • unassign region , 将region从原来的RegionServer上解除绑定;
  • assign region ,将region绑定到目标RegionServer上;

其中, unassign region的具体流程为:
  • create zk closing node . 该节点在/unassigned路径下, 包含(znode状态,region名字,原始RS名,payload)这些数据。
  • hmaster 调用rpc服务关闭region server。region-close的流程大致为先获取region的writeLock , 然后flush memstore, 再并发关闭该region下的所有的store file文件(注意一个region有多个store,每个store又有多个store file , 所以可以实现并发close store file) 。最后释放region的writeLock.
  • 设置zk closing node的znode状态为closed.

assgin region的具体流程为:
  • 获取到对应的Region Plan.
  • HMaster调用rpc服务去Region Plan对应的RegionServer上open region. 这里会先更新/unassigned节点为opening. 然后并发Load HStore,再更行zk/ROOT/META表信息,这里是为了client下次能获取到正确的路由信息, 最后更新region状态为OPEN.

DefaultLoadBalancer策略
这种策略能够保证每个RS的regions个数基本上都相等,确切来说,假设一共有n个RS,第i个RS有Ai个region,记average=sigma(Ai)/n , 那么这种策略能够保证所有的RS的region个数都在[floor(average), ceil(average)]之间。这种策略的实现简单,应用广泛。
但是,这种策略考虑的因素比较单一, 没有考虑到每台region server的读写qps/负载压力等等,这样就可能导致出现一种情况:虽然每个region server的regions都非常接近,但是90%的请求还是落在了一台RS上,因为这台RS上的region全部都是热点数据,这样还是没有达到负载均衡的目的。 但我觉得balance的首要目的是保证数据均衡,如果在数据均衡的情况下,负载还是集中,这时候就要考虑下rowKey的选择是否有问题了。因此, 我个人还是比较推荐采用DefaultLoadBalancer的。StochasticLoadBalancer策略
StochasticLoadBalancer 这种策略真的是非常复杂,简单来讲,是一种综合权衡一下6个因素的均衡策略:
  • 每台RegionServer读请求数(ReadRequestCostFunction)
  • 每台RegionServer写请求数(WriteRequestCostFunction)
  • 每台RegionServer的Region个数(RegionCountSkewCostFunction)
  • 移动代价(MoveCostFunction)
  • 数据locality(TableSkewCostFunction)
  • 每张表占据RegionServer中region个数上限(LocalityCostFunction)

对于cluster的每一种region分布, 采用6个因素加权的方式算出一个代价值,这个代价值就用来评估当前region分布是否均衡,越均衡则代价值越低。然后通过成千上万次随机迭代来找到一组RegionMove的序列,使得最终的代价值严格递减。 得到的这一组RegionMove就是HMaster最终执行的region迁移方案。
这里用一段伪代码来描述这个迭代的过程:
[code]currentCost = MAX ; 
plans = []
for(step = 0 ; step < 1000000; step ++ ){
action = cluster.generateMove()
doAction( action );
newCost = computeCost(action) ;
if (newCost < currentCost){
currentCost = newCost;
plans.add( action );
}else{
undoAction(action);
}
}
[/code]
其中generateMove()每次随机选择以下3种策略中的一种来生成RegionMove:
  1. 随机选择两个RS, 从每个RS中随机选择两个Region,然后生成一个Action, 这个Action有一半概率做RegionMove(从Region多的RS迁移到Region少的RS), 另一半概率做RegionSwap(两个RS之间做Region的交换)。
  2. 选择Region最多的RS和Region最少的RS,然后生成一个Action, 这个Action一半概率做RegionMove, 一半概率做RegionSwap。
  3. 随机找一个RS,然后找到该RS上数据locality最差的Region,再找到Region大部分数据落在的RS,然后生成一个Action,该Action用来把Region迁移到它应该所在的RS,用来提高locality.

对于这种策略,JavaDoc上说效果比较好,但其中的合理性个人觉得有待测试数据的证明(官方基本没有给出这方面的测试结果)。如果6个因素每个参数占据的权重如果没有调好的话,会导致线上的Region大量不均衡。按照我的一次线上经历,采用如下blance配置,出现过每次balance都只选择60个左右的plan去移动, 但真实的情况是145个RS,其中region数量最多的有700+个, 最少的region数量有2个,然后其他RS的region数量在2~700不等,这时候按理来讲应该需要进行大量的balance,但HMaster每隔一个period只生成60个plan左右去移动,这样balance太慢导致很长一段时间内负载不均,有的RS非常清闲,有的RS非常繁忙经常超时。
[code]hbase.master.loadbalancer.class=\
org.apache.hadoop.hbase.master.StochasticLoadBalancer
hbase.master.balancer.stochastic.regionCountCost=10
hbase.master.balancer.stochastic.tableSkewCost=5
hbase.master.balancer.stochastic.readRequestCost=5
hbase.master.balancer.stochastic.writeRequestCost=5
hbase.master.balancer.stochastic.localityCost=10
hbase.master.balancer.stochastic.moveCost=4
hbase.master.balancer.stochastic.maxMovePercent=1
[/code]
后面对比了下了官方的默认配置,应该是regionCountCost一项权重太低, 但是,我想说的是除非线下有一个测试结果支撑具体的权重配置下 balance是符合预期的,否则线上操作时一般对权重很难有一个准确的把握,所以像这么复杂的策略还是要比较谨慎的选择,最好有过历史测试数据来评估balance的效果。

HBase在滴滴出行的应用场景和最佳实践

hbase过往记忆 发表了文章 • 1 个评论 • 574 次浏览 • 2018-10-23 10:27 • 来自相关话题

作者简介:李扬,滴滴出行资深软件开发工程师。2015年加入滴滴出行基础平台部,主要负责HBase和Phoenix以及相关分布式存储技术。在滴滴之前,曾在新浪担任数据工程师,专注于分布式计算和存储。 
责编:郭芮(guorui@csdn.net),关注大数据领域。 
 背景
对接业务类型

HBase是建立在Hadoop生态之上的Database,源生对离线任务支持友好,又因为LSM树是一个优秀的高吞吐数据库结构,所以同时也对接了很多线上业务。在线业务对访问延迟敏感,并且访问趋向于随机,如订单、客服轨迹查询。离线业务通常是数仓的定时大批量处理任务,对一段时间内的数据进行处理并产出结果,对任务完成的时间要求不是非常敏感,并且处理逻辑复杂,如天级别报表、安全和用户行为分析、模型训练等。

多语言支持

HBase提供了多语言解决方案,并且由于滴滴各业务线RD所使用的开发语言各有偏好,所以多语言支持对于HBase在滴滴内部的发展是至关重要的一部分。我们对用户提供了多种语言的访问方式:HBase Java native API、Thrift Server(主要应用于C++、PHP、Python)、JAVA JDBC(Phoenix JDBC)、Phoenix QueryServer(Phoenix对外提供的多语言解决方案)、MapReduce Job(Htable/Hfile Input)、Spark Job、Streaming等。

数据类型

HBase在滴滴主要存放了以下四种数据类型:
 
统计结果、报表类数据:主要是运营、运力情况、收入等结果,通常需要配合Phoenix进行SQL查询。数据量较小,对查询的灵活性要求高,延迟要求一般。原始事实类数据:如订单、司机乘客的GPS轨迹、日志等,主要用作在线和离线的数据供给。数据量大,对一致性和可用性要求高,延迟敏感,实时写入,单点或批量查询。中间结果数据:指模型训练所需要的数据等。数据量大,可用性和一致性要求一般,对批量查询时的吞吐量要求高。线上系统的备份数据:用户把原始数据存在了其他关系数据库或文件服务,把HBase作为一个异地容灾的方案。
 使用场景介绍
场景一:订单事件

这份数据使用过滴滴产品的用户应该都接触过,就是App上的历史订单。近期订单的查询会落在Redis,超过一定时间范围,或者当Redis不可用时,查询会落在HBase上。业务方的需求如下:
 
在线查询订单生命周期的各个状态,包括status、event_type、order_detail等信息。主要的查询来自于客服系统。在线历史订单详情查询。上层会有Redis来存储近期的订单,当Redis不可用或者查询范围超出Redis,查询会直接落到HBase。离线对订单的状态进行分析。写入满足每秒10K的事件,读取满足每秒1K的事件,数据要求在5s内可用。





按照这些要求,我们对Rowkey做出了下面的设计,都是很典型的scan场景。

订单状态表

Rowkey:reverse(order_id) + (MAX_LONG - TS) 
Columns:该订单各种状态

订单历史表

Rowkey:reverse(passenger_id | driver_id) + (MAX_LONG - TS) 
Columns:用户在时间范围内的订单及其他信息

场景二:司机乘客轨迹

这也是一份滴滴用户关系密切的数据,线上用户、滴滴的各个业务线和分析人员都会使用。举几个使用场景上的例子:用户查看历史订单时,地图上显示所经过的路线;发生司乘纠纷,客服调用订单轨迹复现场景;地图部门用户分析道路拥堵情况。





用户们提出的需求:
 
满足App用户或者后端分析人员的实时或准实时轨迹坐标查询;满足离线大规模的轨迹分析;满足给出一个指定的地理范围,取出范围内所有用户的轨迹或范围内出现过的用户。
 其中,关于第三个需求,地理位置查询,我们知道MongoDB对于这种地理索引有源生的支持,但是在滴滴这种量级的情况下可能会发生存储瓶颈,HBase存储和扩展性上没有压力但是没有内置类似MongoDB地理位置索引的功能,没有就需要我们自己实现。通过调研,了解到关于地理索引有一套比较通用的GeohHash算法 。

GeoHash是将二维的经纬度转换成字符串,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,比如说我在悠唐酒店,我的一个朋友在旁边的悠唐购物广场,我们的经纬度点会得到相同的GeoHash串。这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。





但是我们要查询的范围和GeohHash块可能不会完全重合。以圆形为例,查询时会出现如图4所示的一半在GeoHash块内,一半在外面的情况(如A、B、C、D、E、F、G等点)。这种情况就需要对GeoHash块内每个真实的GPS点进行第二次的过滤,通过原始的GPS点和圆心之间的距离,过滤掉不符合查询条件的数据。





最后依据这个原理,把GeoHash和其他一些需要被索引的维度拼装成Rowkey,真实的GPS点为Value,在这个基础上封装成客户端,并且在客户端内部对查询逻辑和查询策略做出速度上的大幅优化,这样就把HBase变成了一个MongoDB一样支持地理位置索引的数据库。如果查询范围非常大(比如进行省级别的分析),还额外提供了MR的获取数据的入口。

两种查询场景的Rowkey设计如下:
 
单个用户按订单或时间段查询: reverse(user_id) + (Integer.MAX_LONG-TS/1000)给定范围内的轨迹查询:reverse(geohash) + ts/1000 + user_id
 场景三:ETA

ETA是指每次选好起始和目的地后,提示出的预估时间和价格。提示的预估到达时间和价格,最初版本是离线方式运行,后来改版通过HBase实现实时效果,把HBase当成一个KeyValue缓存,带来了减少训练时间、可多城市并行、减少人工干预的好处。 
整个ETA的过程如下:
 
模型训练通过Spark Job,每30分钟对各个城市训练一次;模型训练第一阶段,在5分钟内,按照设定条件从HBase读取所有城市数据;模型训练第二阶段在25分钟内完成ETA的计算;HBase中的数据每隔一段时间会持久化至HDFS中,供新模型测试和新的特征提取。
 Rowkey:salting+cited+type0+type1+type2+TS 
Column:order, feature





场景四:监控工具DCM

用于监控Hadoop集群的资源使用(Namenode,Yarn container使用等),关系数据库在时间维度过程以后会产生各种性能问题,同时我们又希望可以通过SQL做一些分析查询,所以使用Phoenix,使用采集程序定时录入数据,生产成报表,存入HBase,可以在秒级别返回查询结果,最后在前端做展示。





图7、图8、图9是几张监控工具的用户UI,数字相关的部分做了模糊处理。















滴滴在HBase对多租户的管理
我们认为单集群多租户是最高效和节省精力的方案,但是由于HBase对多租户基本没有管理,使用上会遇到很多问题:在用户方面比如对资源使用情况不做分析、存储总量发生变化后不做调整和通知、项目上线下线没有计划、想要最多的资源和权限等;我们平台管理者也会遇到比如线上沟通难以理解用户的业务、对每个接入HBase的项目状态不清楚、不能判断出用户的需求是否合理、多租户在集群上发生资源竞争、问题定位和排查时间长等。

针对这些问题,我们开发了DHS系统(Didi HBase Service)进行项目管理,并且在HBase上通过Namespace、RS Group等技术来分割用户的资源、数据和权限。通过计算开销并计费的方法来管控资源分配。





DHS主要有下面几个模块和功能:
 
项目生命周期管理:包括立项、资源预估和申请、项目需求调整、需求讨论;用户管理:权限管理,项目审批;集群资源管理;表级别的使用情况监控:主要是读写监控、memstore、blockcache、locality。
 当用户有使用HBase存储的需求,我们会让用户在DHS上注册项目。介绍业务的场景和产品相关的细节,以及是否有高SLA要求。

之后是新建表以及对表性能需求预估,我们要求用户对自己要使用的资源有一个准确的预估。如果用户难以估计,我们会以线上或者线下讨论的方式与用户讨论帮助确定这些信息。 
然后会生成项目概览页面,方便管理员和用户进行项目进展的跟踪。

HBase自带的jxm信息会汇总到Region和RegionServer级别的数据,管理员会经常用到,但是用户却很少关注这个级别。根据这种情况我们开发了HBase表级别的监控,并且会有权限控制,让业务RD只能看到和自己相关的表,清楚自己项目表的吞吐及存储占用情况。

通过DHS让用户明确自己使用资源情况的基础之上,我们使用了RS Group技术,把一个集群分成多个逻辑子集群,可以让用户选择独占或者共享资源。共享和独占各有自己的优缺点,如表1。





根据以上的情况,我们在资源分配上会根据业务的特性来选择不同方案:
 
对于访问延迟要求低、访问量小、可用性要求低、备份或者测试阶段的数据:使用共享资源池;对于延迟敏感、吞吐要求高、高峰时段访问量大、可用性要求高、在线业务:让其独占一定机器数量构成的RegionServer Group资源,并且按用户预估的资源量,额外给出20%~30%的余量。
 最后我们会根据用户对资源的使用,定期计算开销并向用户发出账单。

RS Group
RegionServer Group,实现细节可以参照HBase HBASE-6721这个Patch。滴滴在这个基础上作了一些分配策略上的优化,以便适合滴滴业务场景的修改。RS Group简单概括是指通过分配一批指定的RegionServer列表,成为一个RS Group,每个Group可以按需挂载不同的表,并且当Group内的表发生异常后,Region不会迁移到其他的Group。这样,每个Group就相当于一个逻辑上的子集群,通过这种方式达到资源隔离的效果,降低管理成本,不必为每个高SLA的业务线单独搭集群。





总结
在滴滴推广和实践HBase的工作中,我们认为至关重要的两点是帮助用户做出良好的表结构设计和资源的控制。有了这两个前提之后,后续出现问题的概率会大大降低。良好的表结构设计需要用户对HBase的实现有一个清晰的认识,大多数业务用户把更多精力放在了业务逻辑上,对架构实现知之甚少,这就需要平台管理者去不断帮助和引导,有了好的开端和成功案例后,通过这些用户再去向其他的业务方推广。资源隔离控制则帮助我们有效减少集群的数量,降低运维成本,让平台管理者从多集群无止尽的管理工作中解放出来,将更多精力投入到组件社区跟进和平台管理系统的研发工作中,使业务和平台都进入一个良性循环,提升用户的使用体验,更好地支持公司业务的发展。
  查看全部
作者简介:李扬,滴滴出行资深软件开发工程师。2015年加入滴滴出行基础平台部,主要负责HBase和Phoenix以及相关分布式存储技术。在滴滴之前,曾在新浪担任数据工程师,专注于分布式计算和存储。 
责编:郭芮(guorui@csdn.net),关注大数据领域。 
 背景
对接业务类型

HBase是建立在Hadoop生态之上的Database,源生对离线任务支持友好,又因为LSM树是一个优秀的高吞吐数据库结构,所以同时也对接了很多线上业务。在线业务对访问延迟敏感,并且访问趋向于随机,如订单、客服轨迹查询。离线业务通常是数仓的定时大批量处理任务,对一段时间内的数据进行处理并产出结果,对任务完成的时间要求不是非常敏感,并且处理逻辑复杂,如天级别报表、安全和用户行为分析、模型训练等。

多语言支持

HBase提供了多语言解决方案,并且由于滴滴各业务线RD所使用的开发语言各有偏好,所以多语言支持对于HBase在滴滴内部的发展是至关重要的一部分。我们对用户提供了多种语言的访问方式:HBase Java native API、Thrift Server(主要应用于C++、PHP、Python)、JAVA JDBC(Phoenix JDBC)、Phoenix QueryServer(Phoenix对外提供的多语言解决方案)、MapReduce Job(Htable/Hfile Input)、Spark Job、Streaming等。

数据类型

HBase在滴滴主要存放了以下四种数据类型:
 
  • 统计结果、报表类数据:主要是运营、运力情况、收入等结果,通常需要配合Phoenix进行SQL查询。数据量较小,对查询的灵活性要求高,延迟要求一般。
  • 原始事实类数据:如订单、司机乘客的GPS轨迹、日志等,主要用作在线和离线的数据供给。数据量大,对一致性和可用性要求高,延迟敏感,实时写入,单点或批量查询。
  • 中间结果数据:指模型训练所需要的数据等。数据量大,可用性和一致性要求一般,对批量查询时的吞吐量要求高。
  • 线上系统的备份数据:用户把原始数据存在了其他关系数据库或文件服务,把HBase作为一个异地容灾的方案。

 使用场景介绍
场景一:订单事件

这份数据使用过滴滴产品的用户应该都接触过,就是App上的历史订单。近期订单的查询会落在Redis,超过一定时间范围,或者当Redis不可用时,查询会落在HBase上。业务方的需求如下:
 
  • 在线查询订单生命周期的各个状态,包括status、event_type、order_detail等信息。主要的查询来自于客服系统。
  • 在线历史订单详情查询。上层会有Redis来存储近期的订单,当Redis不可用或者查询范围超出Redis,查询会直接落到HBase。
  • 离线对订单的状态进行分析。
  • 写入满足每秒10K的事件,读取满足每秒1K的事件,数据要求在5s内可用。


592fd58ff297e.png

按照这些要求,我们对Rowkey做出了下面的设计,都是很典型的scan场景。

订单状态表

Rowkey:reverse(order_id) + (MAX_LONG - TS) 
Columns:该订单各种状态

订单历史表

Rowkey:reverse(passenger_id | driver_id) + (MAX_LONG - TS) 
Columns:用户在时间范围内的订单及其他信息

场景二:司机乘客轨迹

这也是一份滴滴用户关系密切的数据,线上用户、滴滴的各个业务线和分析人员都会使用。举几个使用场景上的例子:用户查看历史订单时,地图上显示所经过的路线;发生司乘纠纷,客服调用订单轨迹复现场景;地图部门用户分析道路拥堵情况。

592fd62ec3aa9.png

用户们提出的需求:
 
  • 满足App用户或者后端分析人员的实时或准实时轨迹坐标查询;
  • 满足离线大规模的轨迹分析;
  • 满足给出一个指定的地理范围,取出范围内所有用户的轨迹或范围内出现过的用户。

 其中,关于第三个需求,地理位置查询,我们知道MongoDB对于这种地理索引有源生的支持,但是在滴滴这种量级的情况下可能会发生存储瓶颈,HBase存储和扩展性上没有压力但是没有内置类似MongoDB地理位置索引的功能,没有就需要我们自己实现。通过调研,了解到关于地理索引有一套比较通用的GeohHash算法 。

GeoHash是将二维的经纬度转换成字符串,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,比如说我在悠唐酒店,我的一个朋友在旁边的悠唐购物广场,我们的经纬度点会得到相同的GeoHash串。这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。

592fd6715b13f.png

但是我们要查询的范围和GeohHash块可能不会完全重合。以圆形为例,查询时会出现如图4所示的一半在GeoHash块内,一半在外面的情况(如A、B、C、D、E、F、G等点)。这种情况就需要对GeoHash块内每个真实的GPS点进行第二次的过滤,通过原始的GPS点和圆心之间的距离,过滤掉不符合查询条件的数据。

592fd6ba65517.png

最后依据这个原理,把GeoHash和其他一些需要被索引的维度拼装成Rowkey,真实的GPS点为Value,在这个基础上封装成客户端,并且在客户端内部对查询逻辑和查询策略做出速度上的大幅优化,这样就把HBase变成了一个MongoDB一样支持地理位置索引的数据库。如果查询范围非常大(比如进行省级别的分析),还额外提供了MR的获取数据的入口。

两种查询场景的Rowkey设计如下:
 
  • 单个用户按订单或时间段查询: reverse(user_id) + (Integer.MAX_LONG-TS/1000)
  • 给定范围内的轨迹查询:reverse(geohash) + ts/1000 + user_id

 场景三:ETA

ETA是指每次选好起始和目的地后,提示出的预估时间和价格。提示的预估到达时间和价格,最初版本是离线方式运行,后来改版通过HBase实现实时效果,把HBase当成一个KeyValue缓存,带来了减少训练时间、可多城市并行、减少人工干预的好处。 
整个ETA的过程如下:
 
  • 模型训练通过Spark Job,每30分钟对各个城市训练一次;
  • 模型训练第一阶段,在5分钟内,按照设定条件从HBase读取所有城市数据;
  • 模型训练第二阶段在25分钟内完成ETA的计算;
  • HBase中的数据每隔一段时间会持久化至HDFS中,供新模型测试和新的特征提取。

 Rowkey:salting+cited+type0+type1+type2+TS 
Column:order, feature

592fd708c4246.png

场景四:监控工具DCM

用于监控Hadoop集群的资源使用(Namenode,Yarn container使用等),关系数据库在时间维度过程以后会产生各种性能问题,同时我们又希望可以通过SQL做一些分析查询,所以使用Phoenix,使用采集程序定时录入数据,生产成报表,存入HBase,可以在秒级别返回查询结果,最后在前端做展示。

592fd7375c265.png

图7、图8、图9是几张监控工具的用户UI,数字相关的部分做了模糊处理。

592fd7610405d.png


592fd77d8ebbe.png


592fd7997641f.png

滴滴在HBase对多租户的管理
我们认为单集群多租户是最高效和节省精力的方案,但是由于HBase对多租户基本没有管理,使用上会遇到很多问题:在用户方面比如对资源使用情况不做分析、存储总量发生变化后不做调整和通知、项目上线下线没有计划、想要最多的资源和权限等;我们平台管理者也会遇到比如线上沟通难以理解用户的业务、对每个接入HBase的项目状态不清楚、不能判断出用户的需求是否合理、多租户在集群上发生资源竞争、问题定位和排查时间长等。

针对这些问题,我们开发了DHS系统(Didi HBase Service)进行项目管理,并且在HBase上通过Namespace、RS Group等技术来分割用户的资源、数据和权限。通过计算开销并计费的方法来管控资源分配。

592fd7f8d4da8.png

DHS主要有下面几个模块和功能:
 
  • 项目生命周期管理:包括立项、资源预估和申请、项目需求调整、需求讨论;
  • 用户管理:权限管理,项目审批;
  • 集群资源管理;
  • 表级别的使用情况监控:主要是读写监控、memstore、blockcache、locality。

 当用户有使用HBase存储的需求,我们会让用户在DHS上注册项目。介绍业务的场景和产品相关的细节,以及是否有高SLA要求。

之后是新建表以及对表性能需求预估,我们要求用户对自己要使用的资源有一个准确的预估。如果用户难以估计,我们会以线上或者线下讨论的方式与用户讨论帮助确定这些信息。 
然后会生成项目概览页面,方便管理员和用户进行项目进展的跟踪。

HBase自带的jxm信息会汇总到Region和RegionServer级别的数据,管理员会经常用到,但是用户却很少关注这个级别。根据这种情况我们开发了HBase表级别的监控,并且会有权限控制,让业务RD只能看到和自己相关的表,清楚自己项目表的吞吐及存储占用情况。

通过DHS让用户明确自己使用资源情况的基础之上,我们使用了RS Group技术,把一个集群分成多个逻辑子集群,可以让用户选择独占或者共享资源。共享和独占各有自己的优缺点,如表1。

592fd853ebaac.png

根据以上的情况,我们在资源分配上会根据业务的特性来选择不同方案:
 
  • 对于访问延迟要求低、访问量小、可用性要求低、备份或者测试阶段的数据:使用共享资源池;
  • 对于延迟敏感、吞吐要求高、高峰时段访问量大、可用性要求高、在线业务:让其独占一定机器数量构成的RegionServer Group资源,并且按用户预估的资源量,额外给出20%~30%的余量。

 最后我们会根据用户对资源的使用,定期计算开销并向用户发出账单。

RS Group
RegionServer Group,实现细节可以参照HBase HBASE-6721这个Patch。滴滴在这个基础上作了一些分配策略上的优化,以便适合滴滴业务场景的修改。RS Group简单概括是指通过分配一批指定的RegionServer列表,成为一个RS Group,每个Group可以按需挂载不同的表,并且当Group内的表发生异常后,Region不会迁移到其他的Group。这样,每个Group就相当于一个逻辑上的子集群,通过这种方式达到资源隔离的效果,降低管理成本,不必为每个高SLA的业务线单独搭集群。

592fd8a900ab5.png

总结
在滴滴推广和实践HBase的工作中,我们认为至关重要的两点是帮助用户做出良好的表结构设计和资源的控制。有了这两个前提之后,后续出现问题的概率会大大降低。良好的表结构设计需要用户对HBase的实现有一个清晰的认识,大多数业务用户把更多精力放在了业务逻辑上,对架构实现知之甚少,这就需要平台管理者去不断帮助和引导,有了好的开端和成功案例后,通过这些用户再去向其他的业务方推广。资源隔离控制则帮助我们有效减少集群的数量,降低运维成本,让平台管理者从多集群无止尽的管理工作中解放出来,将更多精力投入到组件社区跟进和平台管理系统的研发工作中,使业务和平台都进入一个良性循环,提升用户的使用体验,更好地支持公司业务的发展。
 

消灭毛刺!HBase2.0全链路offheap效果拔群

hbase过往记忆 发表了文章 • 0 个评论 • 474 次浏览 • 2018-10-17 19:02 • 来自相关话题

本文原文:https://yq.aliyun.com/articles/653642
阿里云HBase2.0版本正式上线

阿里云HBase2.0版本是基于社区2018年发布的HBase2.0.0版本开发的全新版本。在社区HBase2.0.0版本基础上,做了大量的改进和优化,吸收了众多阿里内部成功经验,比社区HBase版本具有更好的稳定性和性能,同时具备了HBase2.0提供的全新能力。HBase2.0提供的新功能介绍可以参照这篇文章。如果想要申请使用全新的HBase2.0版本,可以在此链接申请试用。在HBase2.0提供的众多功能中,最引人注目的就是全链路的offheap能力了。根据HBase社区官方文档的说法,全链路的offheap功能能够显著减少JVM heap里的数据生成和拷贝,减少垃圾的产生,减少GC的停顿时间。

在线业务在使用hbase读写数据时,我们可能会发现,HBase的平均延迟会很低,可能会低于1ms,但P999延迟(99.9%请求返回的最大时间)可能会高达数百ms。这就是所谓的"毛刺",这些毛刺可能会造成我们的在线业务出现部分请求超时,造成服务质量的下降。而对于HBase来说,GC的停顿,很多时候是造成这样的毛刺的“罪非祸首”。那HBase2.0中的全链路offheap对减少GC停顿,降低P999延迟,真的有那么神奇的功效吗?

全链路offheap原理

在HBase的读和写链路中,均会产生大量的内存垃圾和碎片。比如说写请求时需要从Connection的ByteBuffer中拷贝数据到KeyValue结构中,在把这些KeyValue结构写入memstore时,又需要将其拷贝到MSLAB中,WAL Edit的构建,Memstore的flush等等,都会产生大量的临时对象,和生命周期结束的对象。随着写压力的上升,GC的压力也会越大。读链路也同样存在这样的问题,cache的置换,block数据的decoding,写网络中的拷贝等等过程,都会无形中加重GC的负担。而HBase2.0中引入的全链路offheap功能,正是为了解决这些GC问题。大家知道Java的内存分为onheap和offheap,而GC只会整理onheap的堆。全链路Offheap,就意味着HBase在读写过程中,KeyValue的整个生命周期都会在offheap中进行,HBase自行管理offheap的内存,减少GC压力和GC停顿。

写链路的offheap包括以下几个优化:
在RPC层直接把网络流上的KeyValue读入offheap的bytebuffer中使用offheap的MSLAB pool使用支持offheap的Protobuf版本(3.0+) 

读链路的offheap主要包括以下几个优化:
对BucketCache引用计数,避免读取时的拷贝使用ByteBuffer做为服务端KeyValue的实现,从而使KeyValue可以存储在offheap的内存中对BucketCache进行了一系列性能优化

对比测试

全链路offheap效果怎么样,是骡子是马,都要拿出来试试了。测试的准备工作和相关参数如下:

HBase版本

本次测试选用的1.x版本是云HBase1.1版本截止目前为止最新的AliHB-1.4.9版本,2.x版本是云HBase2.0版本截止目前为止最新的AliHB-2.0.1。这里所有的版本号均为阿里内部HBase分支——AliHB的版本号,与社区的版本号无任何关联。

机型

所有的测试都是针对一台8核16G的ECS机器上部署的RegionServer。底层的HDFS共有两个datanode(副本数为2),其中一个与该RegionServer部署在同一台。每个datanode节点挂载了4块150GB的SSD云盘

测试工具

本次测试所用的是hbase自带的pe工具,由于原生的PE工具不支持不支持单行put和指定batch put数量,因此我对PE工具做了一定的改造,并回馈给了社区,具体内容和使用方法参见这篇文章。

表属性

测试表的分区为64个,compression算法为SNAPPY,Encoding设置为NONE。所有的region都只在一台RegionServer上。

相关的HBase参数

共同参数
HBase的heap大小为9828MB,其中新生代区大小为1719MB使用的GC算法为CMS GC,当老年代占用大小超过75%时开始CMS GC。hfile.block.cache.size 为0.4, 也就是说默认的lru cache的大小为3931.2MBhbase.regionserver.global.memstore.size 为0.35, 即默认的memstore的大小为3439.8MB开启了读写分离,在做写相关的测试时,写线程为90个,读线程为10个。在做读相关测试时(包括读写混合),写线程为20个,读线程为80个

HBase2.xoffheap相关参数

在测写场景时,使用了HBase2.x的默认参数,即只开启了RPC链路上的offheap,并没有开始memstore的offheap。因为根据测试,我们发现开启memstore的offheap并没有带来多大改善,究其原因,还是因为Memstore的offheap只是把KeyValue数据offheap,而Memstore本身使用的Java原生的ConcurrentSkipListMap,其索引结构会在JVM的heap中产生大量的内存碎片,因此只把KeyValue offheap的效果并不是很明显。毕竟,在HBase-1.x开始,就有了MSLAB来管理Memstore中的KeyValue对象,内存结构已经比较紧凑。

在测读场景时:
hbase-env.sh中设置HBASE_OFFHEAPSIZE=5G (RPC和HDFS 客户端需要部分DirectMemory)hbase.bucketcache.ioengine 调成offheaphbase.bucketcache.size 调成 3911,即使用3911MB的DirectMemory来做L2 的cache来 cache data block(之前的测试发现L1中meta block index block的大小大约为20MB,所以在原来onheap的cache基础上减去了20MB)由于cahce的一部分放入offheap,heapsize减至6290MBblock cache的比例不变,用来做L1 cache来cache META block(可能远远大约meta block的需求,但测试中只需保证meta block 100%命中即可,大了不会影响测试)


注意,本次测试旨在测试HBase2.x与HBase1.x版本在相同压力下延迟和GC的表现情况,并非测试HBase的最大吞吐能力,因此测试所用的客户端线程数也只限制在了60~64个,远没有达到云HBase的最大吞吐能力

单行写场景

单行写测试时使用PE工具开启64个写线程,每个写线程随机往HBase表中写入150000行,共960w行。每行的value size为200bytes。所用的PE命令为hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=150000 --autoFlush=true --presplit=64 randomWrite 64








可以看到,使用了HBase-2.x的写链路offheap后,单行写的P999延迟从45ms降低到了5ms,效果非常明显。同时吞吐有5%的提升,带来这种效果的原因就是写链路的offheap使HBase在heap的young区减少了临时对象的产生,younggc发生的频率从0.6次每秒降低到了0.28次每秒。这样受到younggc影响的请求量也会大大减少。因此P999延迟急剧下降.

批量写

在批量写测试中,一次batch的个数是100。使用的命令为:hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=200000 --autoFlush=true --presplit=64 --multiPut=100 randomWrite 64


测试的场景和参数配置与单行写保持一致







可以看到,使用了HBase-2.x的写链路offheap后,从平均延迟到最大延迟,都有不同程度的下降,GC的频率也降到1.x版本的一半以下。因此吞吐也上涨了20%。

100%Cache命中单行Get

在此场景中,先使用以下命令先往表中灌了120w行数据hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=200000 --autoFlush=true --presplit=64 --multiPut=100 sequentialWrite 60


再保证所有数据刷盘,major compact成一个文件后,先做cache的预热,然后使用如下命令进行单行读取: hbase pe --nomapred --oneCon=true --rows=200000 randomRead 60


测试结果如下:






注:百分比的延迟统计最低分辨率是1ms,所以低于1ms时会显示为0

可以看到,在100%内存命中场景下,HBase2.x的吞吐性能有了8%的下滑。这是预料之中的,这在HBase的官方文档中也有解释:读取offheap的内存会比读onheap的内存性能会稍稍下滑。另外,由于在100%内存命中的场景下,onheap的cache也不会发生置换,所以产生的gc开销会比较小,所以在这个场景中,HBase1.x版本的P999延迟也已经比较低。但是,在这个GC不会很严重的场景里(没有写,没有开Block-encoding,cache里内容不用decode可以直接使用),HBase2.x版本仍然可以把最大延迟降到1.x版本的一半,非常难能可贵。

部分cache命中单行读

在这个场景中,先使用以下命令往表中灌了3600w行数据,这些数据会超过设置的cache大小,从而会产生一定的cache miss。
灌数据:hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=600000 --autoFlush=true --presplit=64 --multiPut=100 sequentialWrite 60


再保证所有数据刷盘,major compact成一个文件后,先做cache的预热,然后使用如下命令进行单行读取:hbase pe --nomapred --oneCon=true --rows=600000 randomRead 60








在部分cache命中的场景中,由于会有一定的cahce miss,在读的过程中,会产生cache内容的置换。如果这些内存的置换发生在heap里,会显著加重GC的负担。因此,在这个GC压力比较大的场景中,HBase2.x的全链路读offheap产生了非常优秀的效果,无论是吞吐,平均延迟还是P999和最大延迟,都全面超越HBase1.x版本。由于cache不会在heap中产生垃圾,因此GC的频率和耗时都显著降低,基本消灭了CMSGC。更加难能可贵的是,使用了offheap的bucketcache由于每个bucket都是固定大小,因此在放入不定大小的data block时不可能完全放满,从而会造成一些空间的浪费。因此虽然我把两者的cache大小调到一样的大小,HBase1.x的测试中,data block的命中率有58%,HBase2.x的测试中命中率只有40%。也就是说,HBase2.x在命中率更低的情况下,取得的吞吐和延迟都更加优秀!但这从另外一个方面说明,同样的内存大小,在使用offheap功能后,cache的命中率会降低,因此使用offheap时最好使用速度更高的介质做存储,比如本次测试中选用的SSD云盘。保证读取速度不会被落盘而拖慢太多。

读写混合测试

读写混合测试是大部分生产环境中面对的真实场景。大批量的写和部分命中的读都会产生GC压力,两者一起发生,GC压力可想而知。
在这个测试中,灌数据和读取和部分cache命中场景中使用的命令一致。只不过在读取的同时,在另外一台客户端上起了一个20个线程的批量写测试,去写另外一个Tablehbase pe --nomapred --oneCon=true --valueSize=200 --table=WriteTable --compress=SNAPPY --blockEncoding=DIFF --rows=600000000 --autoFlush=true --presplit=64 --multiPut=100 randomWrite 20







注:表中的QPS指的是读的吞吐

在读写混合测试中,在此压力下,CMS GC的速度已经跟不上heap中产生的垃圾的速度。因此在发生CMS时,由于CMS还没完成时old区已经满(concurrent mode failure),因此CMS GC都退化成了Full GC,从而产生了7到9s的‘stop the world’停顿。因此,1.x中P999被这样的Full GC影响,P999已经上升到了8700ms。而由于HBase2.x使用了读链路offheap。在此场景中仍然稳如泰山,CMS GC发生的频率几乎为0。所以在读写混合场景中,HBase2.x的吞吐是HBase1.x的4倍,P999延迟仍然保持在了100ms之内!

总结

通过上面的测试,我们发现HBase2.x的全链路offheap功能确实能够降低GC停顿时间,在各个场景中,都显示出了非常显著的效果。特别是在部分cache命中和读写混合这两个通常在生产环境中遇到的场景,可谓是效果拔群。所以说HBase2.x中的全链路offheap是我们在生产环境中去降低毛刺,增加吞吐的利器。

云端使用

HBase2.0版本目前已经在阿里云提供商业化服务,任何有需求的用户都可以在阿里云端使用深入改进的、一站式的HBase服务。云HBase版本与自建HBase相比在运维、可靠性、性能、稳定性、安全、成本等方面均有很多的改进,欢迎大家通过下面的连接申请使用阿里云HBase2.0版本,使用全链路offheap这个利器去给生产服务带来更好的稳定性和服务质量。 查看全部
本文原文:https://yq.aliyun.com/articles/653642
阿里云HBase2.0版本正式上线

阿里云HBase2.0版本是基于社区2018年发布的HBase2.0.0版本开发的全新版本。在社区HBase2.0.0版本基础上,做了大量的改进和优化,吸收了众多阿里内部成功经验,比社区HBase版本具有更好的稳定性和性能,同时具备了HBase2.0提供的全新能力。HBase2.0提供的新功能介绍可以参照这篇文章。如果想要申请使用全新的HBase2.0版本,可以在此链接申请试用。在HBase2.0提供的众多功能中,最引人注目的就是全链路的offheap能力了。根据HBase社区官方文档的说法,全链路的offheap功能能够显著减少JVM heap里的数据生成和拷贝,减少垃圾的产生,减少GC的停顿时间。

在线业务在使用hbase读写数据时,我们可能会发现,HBase的平均延迟会很低,可能会低于1ms,但P999延迟(99.9%请求返回的最大时间)可能会高达数百ms。这就是所谓的"毛刺",这些毛刺可能会造成我们的在线业务出现部分请求超时,造成服务质量的下降。而对于HBase来说,GC的停顿,很多时候是造成这样的毛刺的“罪非祸首”。那HBase2.0中的全链路offheap对减少GC停顿,降低P999延迟,真的有那么神奇的功效吗?

全链路offheap原理

在HBase的读和写链路中,均会产生大量的内存垃圾和碎片。比如说写请求时需要从Connection的ByteBuffer中拷贝数据到KeyValue结构中,在把这些KeyValue结构写入memstore时,又需要将其拷贝到MSLAB中,WAL Edit的构建,Memstore的flush等等,都会产生大量的临时对象,和生命周期结束的对象。随着写压力的上升,GC的压力也会越大。读链路也同样存在这样的问题,cache的置换,block数据的decoding,写网络中的拷贝等等过程,都会无形中加重GC的负担。而HBase2.0中引入的全链路offheap功能,正是为了解决这些GC问题。大家知道Java的内存分为onheap和offheap,而GC只会整理onheap的堆。全链路Offheap,就意味着HBase在读写过程中,KeyValue的整个生命周期都会在offheap中进行,HBase自行管理offheap的内存,减少GC压力和GC停顿。

写链路的offheap包括以下几个优化:
  1. 在RPC层直接把网络流上的KeyValue读入offheap的bytebuffer中
  2. 使用offheap的MSLAB pool
  3. 使用支持offheap的Protobuf版本(3.0+) 


读链路的offheap主要包括以下几个优化:
  1. 对BucketCache引用计数,避免读取时的拷贝
  2. 使用ByteBuffer做为服务端KeyValue的实现,从而使KeyValue可以存储在offheap的内存中
  3. 对BucketCache进行了一系列性能优化


对比测试

全链路offheap效果怎么样,是骡子是马,都要拿出来试试了。测试的准备工作和相关参数如下:

HBase版本

本次测试选用的1.x版本是云HBase1.1版本截止目前为止最新的AliHB-1.4.9版本,2.x版本是云HBase2.0版本截止目前为止最新的AliHB-2.0.1。这里所有的版本号均为阿里内部HBase分支——AliHB的版本号,与社区的版本号无任何关联。

机型

所有的测试都是针对一台8核16G的ECS机器上部署的RegionServer。底层的HDFS共有两个datanode(副本数为2),其中一个与该RegionServer部署在同一台。每个datanode节点挂载了4块150GB的SSD云盘

测试工具

本次测试所用的是hbase自带的pe工具,由于原生的PE工具不支持不支持单行put和指定batch put数量,因此我对PE工具做了一定的改造,并回馈给了社区,具体内容和使用方法参见这篇文章。

表属性

测试表的分区为64个,compression算法为SNAPPY,Encoding设置为NONE。所有的region都只在一台RegionServer上。

相关的HBase参数

共同参数
  • HBase的heap大小为9828MB,其中新生代区大小为1719MB
  • 使用的GC算法为CMS GC,当老年代占用大小超过75%时开始CMS GC。
  • hfile.block.cache.size 为0.4, 也就是说默认的lru cache的大小为3931.2MB
  • hbase.regionserver.global.memstore.size 为0.35, 即默认的memstore的大小为3439.8MB
  • 开启了读写分离,在做写相关的测试时,写线程为90个,读线程为10个。在做读相关测试时(包括读写混合),写线程为20个,读线程为80个


HBase2.xoffheap相关参数

在测写场景时,使用了HBase2.x的默认参数,即只开启了RPC链路上的offheap,并没有开始memstore的offheap。因为根据测试,我们发现开启memstore的offheap并没有带来多大改善,究其原因,还是因为Memstore的offheap只是把KeyValue数据offheap,而Memstore本身使用的Java原生的ConcurrentSkipListMap,其索引结构会在JVM的heap中产生大量的内存碎片,因此只把KeyValue offheap的效果并不是很明显。毕竟,在HBase-1.x开始,就有了MSLAB来管理Memstore中的KeyValue对象,内存结构已经比较紧凑。

在测读场景时:
  • hbase-env.sh中设置HBASE_OFFHEAPSIZE=5G (RPC和HDFS 客户端需要部分DirectMemory)
  • hbase.bucketcache.ioengine 调成offheap
  • hbase.bucketcache.size 调成 3911,即使用3911MB的DirectMemory来做L2 的cache来 cache data block(之前的测试发现L1中meta block index block的大小大约为20MB,所以在原来onheap的cache基础上减去了20MB)
  • 由于cahce的一部分放入offheap,heapsize减至6290MB
  • block cache的比例不变,用来做L1 cache来cache META block(可能远远大约meta block的需求,但测试中只需保证meta block 100%命中即可,大了不会影响测试)



注意,本次测试旨在测试HBase2.x与HBase1.x版本在相同压力下延迟和GC的表现情况,并非测试HBase的最大吞吐能力,因此测试所用的客户端线程数也只限制在了60~64个,远没有达到云HBase的最大吞吐能力

单行写场景

单行写测试时使用PE工具开启64个写线程,每个写线程随机往HBase表中写入150000行,共960w行。每行的value size为200bytes。所用的PE命令为
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=150000 --autoFlush=true --presplit=64 randomWrite 64



menu.saveimg_.savepath20181017190804_.jpg


可以看到,使用了HBase-2.x的写链路offheap后,单行写的P999延迟从45ms降低到了5ms,效果非常明显。同时吞吐有5%的提升,带来这种效果的原因就是写链路的offheap使HBase在heap的young区减少了临时对象的产生,younggc发生的频率从0.6次每秒降低到了0.28次每秒。这样受到younggc影响的请求量也会大大减少。因此P999延迟急剧下降.

批量写

在批量写测试中,一次batch的个数是100。使用的命令为:
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=200000 --autoFlush=true --presplit=64 --multiPut=100 randomWrite 64


测试的场景和参数配置与单行写保持一致


menu.saveimg_.savepath20181017191031_.jpg


可以看到,使用了HBase-2.x的写链路offheap后,从平均延迟到最大延迟,都有不同程度的下降,GC的频率也降到1.x版本的一半以下。因此吞吐也上涨了20%。

100%Cache命中单行Get

在此场景中,先使用以下命令先往表中灌了120w行数据
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=200000 --autoFlush=true --presplit=64 --multiPut=100 sequentialWrite  60


再保证所有数据刷盘,major compact成一个文件后,先做cache的预热,然后使用如下命令进行单行读取:
 hbase pe --nomapred --oneCon=true --rows=200000 randomRead   60


测试结果如下:

menu.saveimg_.savepath20181017191142_.jpg


注:百分比的延迟统计最低分辨率是1ms,所以低于1ms时会显示为0

可以看到,在100%内存命中场景下,HBase2.x的吞吐性能有了8%的下滑。这是预料之中的,这在HBase的官方文档中也有解释:读取offheap的内存会比读onheap的内存性能会稍稍下滑。另外,由于在100%内存命中的场景下,onheap的cache也不会发生置换,所以产生的gc开销会比较小,所以在这个场景中,HBase1.x版本的P999延迟也已经比较低。但是,在这个GC不会很严重的场景里(没有写,没有开Block-encoding,cache里内容不用decode可以直接使用),HBase2.x版本仍然可以把最大延迟降到1.x版本的一半,非常难能可贵。

部分cache命中单行读

在这个场景中,先使用以下命令往表中灌了3600w行数据,这些数据会超过设置的cache大小,从而会产生一定的cache miss。
灌数据:
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=600000 --autoFlush=true --presplit=64 --multiPut=100 sequentialWrite  60


再保证所有数据刷盘,major compact成一个文件后,先做cache的预热,然后使用如下命令进行单行读取:
hbase pe --nomapred --oneCon=true --rows=600000 randomRead   60



menu.saveimg_.savepath20181017191308_.jpg


在部分cache命中的场景中,由于会有一定的cahce miss,在读的过程中,会产生cache内容的置换。如果这些内存的置换发生在heap里,会显著加重GC的负担。因此,在这个GC压力比较大的场景中,HBase2.x的全链路读offheap产生了非常优秀的效果,无论是吞吐,平均延迟还是P999和最大延迟,都全面超越HBase1.x版本。由于cache不会在heap中产生垃圾,因此GC的频率和耗时都显著降低,基本消灭了CMSGC。更加难能可贵的是,使用了offheap的bucketcache由于每个bucket都是固定大小,因此在放入不定大小的data block时不可能完全放满,从而会造成一些空间的浪费。因此虽然我把两者的cache大小调到一样的大小,HBase1.x的测试中,data block的命中率有58%,HBase2.x的测试中命中率只有40%。也就是说,HBase2.x在命中率更低的情况下,取得的吞吐和延迟都更加优秀!但这从另外一个方面说明,同样的内存大小,在使用offheap功能后,cache的命中率会降低,因此使用offheap时最好使用速度更高的介质做存储,比如本次测试中选用的SSD云盘。保证读取速度不会被落盘而拖慢太多。

读写混合测试

读写混合测试是大部分生产环境中面对的真实场景。大批量的写和部分命中的读都会产生GC压力,两者一起发生,GC压力可想而知。
在这个测试中,灌数据和读取和部分cache命中场景中使用的命令一致。只不过在读取的同时,在另外一台客户端上起了一个20个线程的批量写测试,去写另外一个Table
hbase pe --nomapred --oneCon=true --valueSize=200 --table=WriteTable  --compress=SNAPPY --blockEncoding=DIFF  --rows=600000000 --autoFlush=true --presplit=64 --multiPut=100 randomWrite 20


menu.saveimg_.savepath20181017191441_.jpg


注:表中的QPS指的是读的吞吐

在读写混合测试中,在此压力下,CMS GC的速度已经跟不上heap中产生的垃圾的速度。因此在发生CMS时,由于CMS还没完成时old区已经满(concurrent mode failure),因此CMS GC都退化成了Full GC,从而产生了7到9s的‘stop the world’停顿。因此,1.x中P999被这样的Full GC影响,P999已经上升到了8700ms。而由于HBase2.x使用了读链路offheap。在此场景中仍然稳如泰山,CMS GC发生的频率几乎为0。所以在读写混合场景中,HBase2.x的吞吐是HBase1.x的4倍,P999延迟仍然保持在了100ms之内!

总结

通过上面的测试,我们发现HBase2.x的全链路offheap功能确实能够降低GC停顿时间,在各个场景中,都显示出了非常显著的效果。特别是在部分cache命中和读写混合这两个通常在生产环境中遇到的场景,可谓是效果拔群。所以说HBase2.x中的全链路offheap是我们在生产环境中去降低毛刺,增加吞吐的利器。

云端使用

HBase2.0版本目前已经在阿里云提供商业化服务,任何有需求的用户都可以在阿里云端使用深入改进的、一站式的HBase服务。云HBase版本与自建HBase相比在运维、可靠性、性能、稳定性、安全、成本等方面均有很多的改进,欢迎大家通过下面的连接申请使用阿里云HBase2.0版本,使用全链路offheap这个利器去给生产服务带来更好的稳定性和服务质量。

HBase Schema 设计

hbase过往记忆 发表了文章 • 0 个评论 • 705 次浏览 • 2018-10-08 11:39 • 来自相关话题

大多数开发小伙伴都熟悉“数据库设计”这个词,在关系数据库的概念中,范式是关键。然而在大数据时代,分布式,可扩展,类似HBase这种nonSQL数据库,打破了传统关系数据库中的某些范式规定。这篇PPT将从大概念上介绍HBase大schema设计理念,并给一些通用的模式和真实的schema设计案例。当然也会探讨一些HBase的基本概念包括rowkey,列族等等,通过理解这些概念而理解在Schema定义的时候我们必须要做的一些取舍。 查看全部
大多数开发小伙伴都熟悉“数据库设计”这个词,在关系数据库的概念中,范式是关键。然而在大数据时代,分布式,可扩展,类似HBase这种nonSQL数据库,打破了传统关系数据库中的某些范式规定。这篇PPT将从大概念上介绍HBase大schema设计理念,并给一些通用的模式和真实的schema设计案例。当然也会探讨一些HBase的基本概念包括rowkey,列族等等,通过理解这些概念而理解在Schema定义的时候我们必须要做的一些取舍。

HBase实战之MOB使用指南

hbase过往记忆 发表了文章 • 0 个评论 • 2329 次浏览 • 2018-10-08 10:29 • 来自相关话题

HBase可以很方便的将图片、文本等文件以二进制的方式进行存储。虽然HBase一般可以处理从1字节到10MB大小的二进制对象,但是HBase通常对于读写路径的优化主要是针对小于100KB的值。当HBase处理数据为100KB~10MB时,由于分裂(split)和压缩(compaction)会引起写的放大,从而会降低HBase性能。所以在HBase2.0+引入了MOB特性,这样保持了HBase的高性能、强一致性和低开销。

若要启用MOB功能,需要在每个RegionServer进行配置,并在建表或者修改表时对指定列族启用MOB特性。在HBase尝鲜版中启用MOB功能,需要由admin用户设置定期进程,以重新优化MOB数据的分布。

启用和配置RegionServer上的MOB特性
 
增加或者修改hbase-site.xml文件中的某些配置

1 设置MOB文件的缓存配置<property>
<name>hbase.mob.file.cache.size</name>
<value>1000</value>
</property>

<property>
<name>hbase.mob.cache.evict.period</name>
<value>3600</value>
</property>

<property>
<name>hbase.mob.cache.evict.remain.ratio</name>
<value>0.5f</value>
</property>

说明:

1)hbase.mob.file.cache.size 打开的文件句柄缓存数,默认值是1000。通过增加文件句柄数可以提高读的性能,可以减少频繁的打开、关闭文件。若这个值设置过大,会导致“too many opened file handers”。

2)hbase.mob.cache.evict.period  MOB缓存淘汰缓存的MOB文件时间间隔(以秒为单位),默认值为3600秒。

3)hbase.mob.cache.evict.remain.ratio   当缓存的MOB文件数目超过hbase.mob.file.cache.size设置的数目后,会触发MOB缓存淘汰机制(eviction),0.5f为剩余的MOB缓存比率(0~1),默认的比率为0.5f。


2  配置MobMasterObserver作为协处理器的master,主要用于表在删除后,MOB文件的归档。<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.hadoop.hbase.coprocessor.MobMasterObserver </value>
</property>

MOB的管理MOB特性为HBase引入新的读写路径,此时我们采用外部工具对其进行优化处理,一个是expiredMobFileCleanerTTL处理TTL和时间的过期数据,另一个是清理工具用来合并小的MOB文件或者多次更新、删除的MOB文件。

  a . 清理过期的MOB数据(expiredMobFileCleaner)

org.apache.hadoop.hbase.mob.compactions.expiredMobFileCleaner tableName familyName

设置清理延时<property>
<name>hbase.mob.cleaner.delay</name>
<value>60 * 60 * 1000</value>
</property>

 b.  清理工具

org.apache.hadoop.hbase.mob.compactions.Sweeper tableName familyName

属性值设置如下:<property>
<name>hbase.mob.compaction.invalid.file.ratio</name>
<value>0.3f</value>
</property>

<property>
<name>hbase.mob.compaction.small.file.threshold</name>
<value>67108864</value>
</property>

<property>
<name>hbase.mob.compaction.memstore.flush.size</name>
<value>134217728</value>
</property>

说明:

1)hbase.mob.compaction.invalid.file.ratio 如果在MOB文件中删除了太多的单元格,则被视为作为无效文件,需要重新写入或者合并。当MOB文件(mobFileSize)大小减去存在的单元格(existingCellsSize)大小之差除以MOB文件(mobFileSize)的比率小于设定的值时,我们就认为其为无效文件。默认值为0.3f。

2)hbase.mob.compaction.small.file.threshold 如果MOB的大小小于阈值,则视为小文件,需要合并。默认值为64MB。

3) hbase.mob.compaction.memstore.flush.size  MOB里memstore大小,超过此大小就会flush,并且每个sweep reducer拥有各自memstore。


警告:

使用清理工具最坏的情况:MOB文件压缩合并成功,但是相关的(put)更新失败。这意味着新的MOB文件已经创建但未能将新的MOB文件路径存入HBase中,因此HBase不会指向这些MOB文件。




小贴士:请检查yarn-site.xml的配置,在yarn.application.classpath中添加hbase的安装路径:$HBASE_HOME/* 和hbase的lib路径: $HBASE_HOME/lib/*

  <property>
<description>Classpath for typical applications.</description>
<name>yarn.application.classpath</name>
<value>
$HADOOP_CONF_DIR
$HADOOP_COMMON_HOME/*,$HADOOP_COMMON_HOME/lib/*
$HADOOP_HDFS_HOME/*,$HADOOP_HDFS_HOME/lib/*,
$HADOOP_MAPRED_HOME/*,$HADOOP_MAPRED_HOME/lib/*
$HADOOP_YARN_HOME/*,$HADOOP_YARN_HOME/lib/*
$HBASE_HOME/*, $HBASE_HOME/lib/*
</value>
</property>
在表中开启MOB特性a 将列族设置为MOBHColumnDescriptor hcd = new HColumnDescriptor(“f”);
hcd.setValue(MobConstants.IS_MOB, Bytes.toBytes(Boolean.TRUE));
b 设置MOB单元格的阈值,默认为102400HColumnDescriptor hcd;
hcd.setValue(MobConstants.MOB_THRESHOLD, Bytes.toBytes(102400L);

对于客户端而言,MOB单元格操作和普通单元格类似。

c 插入MOB值KeyValue kv = new KeyValue(row1, family, qf1, ts, KeyValue.Type.Put, value );
Put put = new Put(row1);
put.add(kv);
region.put(put);


d 获取MOB值Scan scan = new Scan();
InternalScanner scanner = (InternalScanner) region.getScanner(scan);
scanner.next(result, limit);
e 获取MOB 所有数据(raw =true)Scan scan = new Scan();
scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE));
InternalScanner scanner = (InternalScanner) region.getScanner(scan);
scanner.next(result, limit);
运行一个MOB示例sudo -u hbase hbase org.apache.hadoop.hbase.IntegrationTestIngestMOB


  查看全部
HBase可以很方便的将图片、文本等文件以二进制的方式进行存储。虽然HBase一般可以处理从1字节到10MB大小的二进制对象,但是HBase通常对于读写路径的优化主要是针对小于100KB的值。当HBase处理数据为100KB~10MB时,由于分裂(split)和压缩(compaction)会引起写的放大,从而会降低HBase性能。所以在HBase2.0+引入了MOB特性,这样保持了HBase的高性能、强一致性和低开销。

若要启用MOB功能,需要在每个RegionServer进行配置,并在建表或者修改表时对指定列族启用MOB特性。在HBase尝鲜版中启用MOB功能,需要由admin用户设置定期进程,以重新优化MOB数据的分布。

启用和配置RegionServer上的MOB特性
 
增加或者修改hbase-site.xml文件中的某些配置

1 设置MOB文件的缓存配置
<property>
<name>hbase.mob.file.cache.size</name>
<value>1000</value>
</property>

<property>
<name>hbase.mob.cache.evict.period</name>
<value>3600</value>
</property>

<property>
<name>hbase.mob.cache.evict.remain.ratio</name>
<value>0.5f</value>
</property>


说明:

1)hbase.mob.file.cache.size 打开的文件句柄缓存数,默认值是1000。通过增加文件句柄数可以提高读的性能,可以减少频繁的打开、关闭文件。若这个值设置过大,会导致“too many opened file handers”。

2)hbase.mob.cache.evict.period  MOB缓存淘汰缓存的MOB文件时间间隔(以秒为单位),默认值为3600秒。

3)hbase.mob.cache.evict.remain.ratio   当缓存的MOB文件数目超过hbase.mob.file.cache.size设置的数目后,会触发MOB缓存淘汰机制(eviction),0.5f为剩余的MOB缓存比率(0~1),默认的比率为0.5f。


2  配置MobMasterObserver作为协处理器的master,主要用于表在删除后,MOB文件的归档。
<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.hadoop.hbase.coprocessor.MobMasterObserver </value>
</property>


MOB的管理MOB特性为HBase引入新的读写路径,此时我们采用外部工具对其进行优化处理,一个是expiredMobFileCleanerTTL处理TTL和时间的过期数据,另一个是清理工具用来合并小的MOB文件或者多次更新、删除的MOB文件。

  a . 清理过期的MOB数据(expiredMobFileCleaner)

org.apache.hadoop.hbase.mob.compactions.expiredMobFileCleaner tableName familyName

设置清理延时
<property>
<name>hbase.mob.cleaner.delay</name>
<value>60 * 60 * 1000</value>
</property>


 b.  清理工具

org.apache.hadoop.hbase.mob.compactions.Sweeper tableName familyName

属性值设置如下:
<property>
<name>hbase.mob.compaction.invalid.file.ratio</name>
<value>0.3f</value>
</property>

<property>
<name>hbase.mob.compaction.small.file.threshold</name>
<value>67108864</value>
</property>

<property>
<name>hbase.mob.compaction.memstore.flush.size</name>
<value>134217728</value>
</property>

说明:

1)hbase.mob.compaction.invalid.file.ratio 如果在MOB文件中删除了太多的单元格,则被视为作为无效文件,需要重新写入或者合并。当MOB文件(mobFileSize)大小减去存在的单元格(existingCellsSize)大小之差除以MOB文件(mobFileSize)的比率小于设定的值时,我们就认为其为无效文件。默认值为0.3f。

2)hbase.mob.compaction.small.file.threshold 如果MOB的大小小于阈值,则视为小文件,需要合并。默认值为64MB。

3) hbase.mob.compaction.memstore.flush.size  MOB里memstore大小,超过此大小就会flush,并且每个sweep reducer拥有各自memstore。


警告:

使用清理工具最坏的情况:MOB文件压缩合并成功,但是相关的(put)更新失败。这意味着新的MOB文件已经创建但未能将新的MOB文件路径存入HBase中,因此HBase不会指向这些MOB文件。




小贴士:请检查yarn-site.xml的配置,在yarn.application.classpath中添加hbase的安装路径:$HBASE_HOME/* 和hbase的lib路径: $HBASE_HOME/lib/*

  
<property>
<description>Classpath for typical applications.</description>
<name>yarn.application.classpath</name>
<value>
$HADOOP_CONF_DIR
$HADOOP_COMMON_HOME/*,$HADOOP_COMMON_HOME/lib/*
$HADOOP_HDFS_HOME/*,$HADOOP_HDFS_HOME/lib/*,
$HADOOP_MAPRED_HOME/*,$HADOOP_MAPRED_HOME/lib/*
$HADOOP_YARN_HOME/*,$HADOOP_YARN_HOME/lib/*
$HBASE_HOME/*, $HBASE_HOME/lib/*
</value>
</property>

在表中开启MOB特性a 将列族设置为MOB
HColumnDescriptor hcd = new HColumnDescriptor(“f”);
hcd.setValue(MobConstants.IS_MOB, Bytes.toBytes(Boolean.TRUE));

b 设置MOB单元格的阈值,默认为102400
HColumnDescriptor hcd;
hcd.setValue(MobConstants.MOB_THRESHOLD, Bytes.toBytes(102400L);


对于客户端而言,MOB单元格操作和普通单元格类似。

c 插入MOB值
KeyValue kv = new KeyValue(row1, family, qf1, ts, KeyValue.Type.Put, value );
Put put = new Put(row1);
put.add(kv);
region.put(put);


d 获取MOB值
Scan scan = new Scan();
InternalScanner scanner = (InternalScanner) region.getScanner(scan);
scanner.next(result, limit);

e 获取MOB 所有数据(raw =true)
Scan scan = new Scan();
scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE));
InternalScanner scanner = (InternalScanner) region.getScanner(scan);
scanner.next(result, limit);

运行一个MOB示例sudo -u hbase hbase org.apache.hadoop.hbase.IntegrationTestIngestMOB


 

HBase实践之MOB使用指南(未翻译)

hbasebeyond 发表了文章 • 0 个评论 • 735 次浏览 • 2018-10-08 09:08 • 来自相关话题

此版本为未翻译版本,文档来源于hbase官方,
社区相关的MOB阅读: HBase MOB压缩分区策略介绍  
  查看全部
此版本为未翻译版本,文档来源于hbase官方,
社区相关的MOB阅读: HBase MOB压缩分区策略介绍  
 
  

中国HBase技术社区微信公众号:
hbasegroup

欢迎加入HBase生态+Spark社区钉钉大群