HBase coprocessor介绍及使用

hbase过往记忆 发表了文章 • 0 个评论 • 133 次浏览 • 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中的具体使用

头条招聘-数据架构高级开发工程师

招聘hishale 回复了问题 • 3 人关注 • 4 个回复 • 368 次浏览 • 2019-03-27 16:30 • 来自相关话题

phoenix 报错Illegal data. Expected length of at least 48 bytes, but had 19

回复

phoenixshamoshuizu 发起了问题 • 1 人关注 • 0 个回复 • 53 次浏览 • 2019-03-26 10:03 • 来自相关话题

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

hbasechuxiao 发表了文章 • 2 个评论 • 144 次浏览 • 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 gc 调优

hbasebeyond 回复了问题 • 2 人关注 • 1 个回复 • 130 次浏览 • 2019-03-22 17:42 • 来自相关话题

有关导出数据问题

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

hbase regionserver启动不了

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

spark 数据倾斜解决思路?

回复

spark刘狗 发起了问题 • 1 人关注 • 0 个回复 • 106 次浏览 • 2019-03-20 18:28 • 来自相关话题

hbase0.94.11写性能比1.4.9版本的性能好很多,为什么

回复

hbasedh247203650 回复了问题 • 1 人关注 • 2 个回复 • 109 次浏览 • 2019-03-20 15:18 • 来自相关话题

spark读取redis只能返回string类型的RDD,如果redis中存储protobuf序列化后的byte[],spark该怎么获取到结果的类型是byte[]的RDD

回复

spark刘狗 回复了问题 • 1 人关注 • 1 个回复 • 157 次浏览 • 2019-03-19 08:31 • 来自相关话题

翻译phoenix文档

phoenix过往记忆 回复了问题 • 2 人关注 • 1 个回复 • 140 次浏览 • 2019-03-16 20:43 • 来自相关话题

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

hbasechuxiao 发表了文章 • 0 个评论 • 202 次浏览 • 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

 

 

 
 

phoenix创建二级索引表 Error: java.lang.ArrayIndexOutOfBoundsException (state=08000,code=101)

回复

phoenixriyu_liang 回复了问题 • 1 人关注 • 1 个回复 • 157 次浏览 • 2019-03-14 16:16 • 来自相关话题

在spark算子中开辟内存保存结果数据,这样做高效吗

sparkmuyuan 回复了问题 • 2 人关注 • 1 个回复 • 146 次浏览 • 2019-03-13 20:13 • 来自相关话题

phoenix集成mybatis无法连接hbase自定义namespase中的表

回复

phoenixs18675897692 发起了问题 • 2 人关注 • 0 个回复 • 114 次浏览 • 2019-03-12 21:48 • 来自相关话题

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

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

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

hbasechuxiao 发表了文章 • 0 个评论 • 191 次浏览 • 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技术社区微信公众号:
hbasegroup

欢迎加入HBase生态+Spark社区钉钉大群