HBase中JVM基本配置

在JVM中,默认情况下会设置minimum heap size 为 1/64 可用物理内存,并为maximum heap size设置 1/4 的物理可用内存(不过在Java8 之前,默认最大是1g)。当然,我们可以通过手动指定 JVM 参数,配置JVM的内存,例如:

-Xms10g -Xmx10g

在HBase 中,也可以在 hbase-env.sh 中显示指定堆内存大小,例如:

# The maximum amount of heap to use. Default is left to JVM default.

# export HBASE_HEAPSIZE=1G

这里有个问题是:是否要同时设置-Xms 与 -Xmx为一相同的值?若是将他们设置为同一一个固定值,则它们的优缺点有:

  1. 对于一些垃圾回收的算法,例如G1,将它们锁定为同一值会更好
  2. 不设置 -Xms可以让VM启动更快,但是接下来,需要在每轮gc 增长堆内存后,才会达到一个稳定状态。这样会导致在启动后,最开始会带来一些延迟,这个延时并不是我们在HBase中期望看到的,因为HBase提供的服务一般都是交互式服务

若是设置-Xms与-Xmx为同一值,则JVM在启动时会稍慢,不过在正常启动后,不会再有隐含的内存大小调整的情况,所以会更稳定。而在RegionServer 的进程中,我们可以在分配工作给它前,稍等一会而,以缓解启动进程的消耗。

对于HMaster进程,一般不需要太大内存,2GB 到 4GB就可以满足master的工作需求,因为它的对象分配与销毁的频率远小于RegionServer。

对于RegionServer 进程,若是物理机内存为64GB,则一开始可以给它分配10GB的-Xmx,之后可以根据监控(如jstat等)再进行向上调整。不过若是分配了较大的堆内存,我们也必须要考虑不同的GC算法,以支持更大堆内存中的垃圾回收。

基于以上场景,我们可以在hbase-env.sh 中配置以下参数:

# Set environment variables here.
export HBASE_MASTER_OPTS="-Xms2g -Xmx2g"
export HBASE_REGIONSERVER_OPTS="-Xms10g -Xmx10g"

如前文所述,不指定-Xms的话,堆内存需要在增长多次后才能到达最大堆内存,导致在重新分配内存时的不必要开销,所以最好是按上述指定。

HBase中的堆内存管理

在RegionServer中,主要的任务为:接收并处理读写请求。不过除此之外,RegionServer也需要在内存中维护一些内部信息,例如:对于每个打开的region来说,它们都需要在内存里结构化存储,以处理一些常规操作。

在RegionServer启动后,所分配的JVM堆内存大小就已经指定了,无法再增加。所以对于这部分已有的内存,我们需要谨慎使用,其中最重要的一部分就是:如何调整可供读写操作使用的可用内存。

在HBase 1.0 之前,主要有两个参数需要调整,分别为memstores(对于写)以及block cache(对于读)。剩下的内存被用于所有其他杂项使用(例如Java用于存储打开的region的结构)。对于memstore,我们有以下两个参数可以指定:

  • hbase.regionserver.global.memstore.upperLimit默认为0.4(40%),也就是memstore可以使用的最大堆内存
  • hbase.regionserver.global.memstore.lowerLimit默认为0.35(30%),也就是memstore可以使用的最小堆内存

upper limit在这里较为重要,因为它指定了对于写操作,可以使用的最大内存。对于读来说,仅有一个参数可供调整:

  • hfile.block.cache.size:默认为0.4(40%),堆内存中用于分配给block cache的比率

memestore的upperLimit ,与 blockcache 的大小,它们的和不能超过80% 堆内存总大小,因为仍需要提供剩余的20% 内存以上,用于regionserver进程完成它自身的部分工作(并且防止Java OOM的问题)。

在HBase 1.0 之后,提供了更多的配置项,用于指定memstore 与 block cache 的upper limit 与 lower limit,例如:

  • hbase.regionserver.global.memstore.size.min.range默认未设置,为所有memstore的最小分配内存
  • hbase.regionserver.global.memstore.size.max.range默认未设置,为所有memsotre的最大分配内存
  • hbase.regionserver.global.memstore.size默认为0.4(40%),也就是memstore默认被分配的对内存比例
  • hfile.block.cache.size.min.range默认未设置,为block cache 大小的最小值
  • hfile.block.cache.size.max.range默认未设置,为block cache 大小的最大值
  • hfile.block.cache.size默认为0.4(40%),也就是堆内存中默认分配给block cache 的比例
  • hbase.regionserver.heapmemory.tuner.period:默认为60000(60s),定义heap tunner 运行的频率

对于memstore与block cache 的上限与下限,我们必须做配置,除非禁止tuner。

HBase 提供了一个机制是:根据请求流量大小,对堆内存进行动态调整。这就是为什么在开启tuner的情况下,我们必须要设置一个上限与下限。

不过这里仍需要保证的一点是:memstore 与 block cache 的最大使用量不能超过JVM内存的 80%。如果超过了这个比例,则tuner会报错,region server 会退出。这个限制的原因与之前的一样:需要剩余足够的内存供给 region server 进程正常工作。

下图是固定内存与动态内存的一个示例图:

动态内存对应的配置为:

<property>
  <name>hbase.regionserver.global.memstore.size.min.range</name>
  <value>0.2</value>
</property>
<property>
  <name>hbase.regionserver.global.memstore.size.max.range</name>
  <value>0.6</value>
</property>
<property>
  <name>hfile.block.cache.size.min.range</name>
  <value>0.2</value>
</property>
<property>
  <name>hfile.block.cache.size.max.range</name>
  <value>0.6</value>
</property>

它们的初始值为默认的40%内存。Tuner可以在某一个内存(如memstore的内存)达到最小20%后,给另一个分配60%的内存。

Tuner的机制仅是增加某一区域(如memstore)的内存,在增加后另一区域(如block cache)的内存即会减少。基于的是(或是说触发的条件是):由于内存压力导致的memstore 强制刷新(会要求更多内存,即增加此区域内存),或是内存压力导致的block cache 中的cache 置换。

两个主要GC算法

在 HBase 中,主要使用的GC算法为CMS或G1,它们的对比如下:

名称                                                               JVM参数                                        说明

Consurrent Mark Sweep(CMS)            -XX:+UseConcMarkSweepGC      低延时(堆内存 < 32GB)

Garbage First(G1)                                  -XX:+UseG1GC                               低延时

CMS为较早使用的GC 算法,它可以有效提升HBase的性能,特别是I/O请求的延时。不过它的缺点是:有最大内存的限制。

在CMS刚开始被提出的时候,它可以良好适应当时的服务器硬件性能,不过随着现在超出1TB的内存机器越来越常见,32GB大小的堆内存配置也越来越常见。CMS不适用于较大堆内存的原因是因为:在较大的堆内存中,垃圾回收会造成较长时间的停顿(几秒到十几秒),并最终会导致请求的高延时。

JVM stop log

在HBase region server 进程启动后,会启动一个轻量级的线程,定期(500ms)被唤醒,并比较它在唤醒前它睡眠的时间以及实际经过的时间是否一致。如果检测到它实际睡眠的时间超过了500ms,则在服务器日志记录,例如:

2016-08-05 07:11:32,390 INFO  [JvmPauseMonitor] util.JvmPauseMonitor: \
  Detected pause in JVM or host machine (eg GC): \
    pause of approximately 7448ms
GC pool 'ParNew' had collection(s): count=1 time=49ms
GC pool 'ConcurrentMarkSweep' had collection(s): count=1 time=7700ms
...
 
2016-08-11 23:01:56,686 WARN  [JvmPauseMonitor] util.JvmPauseMonitor: \
  Detected pause in JVM or host machine (eg GC): \
    pause of approximately 3594293ms
No GCs detected

第一条是由gc导致,记录的是 CMS 消耗了超过7秒等待垃圾回收完成。第二条与机器的暂停无关。根据暂停的时间,日志会以INFO或是WARN级别记录下来,对应的参数如下:

jvm.pause.info-threshold.ms:默认为1000ms(1s),若是停顿时间超过此值,则以INFO级别记录日志

jvm.pause.warn-threshold.ms:默认为10000ms(10s),若是停顿时间超过此值,则以WARN级别记录日志

在做调优时要注意这些日志条目,以了解停顿的频率与时间。并根据时间调整相关timeout配置(例如zookeeper的配置,如果regionserver 超过一段时间未响应,可能会被zookeeper认为宕机)。

CMS 与 G1

再回到GC部分,G1的提出就是为了解决:CMS在较大堆内存回收垃圾时产生的长时间停顿问题。下图是两者的实现与对比:

在G1 中,相对于CMS预先配置的堆内存容量,它将整个堆内存空间划分为多个相同大小的regions,一般是2000个左右。在划分好后,G1可以让其中的部分region分别执行Young Generation、Old Generation、以及survivor的角色。G1 gc采用的策略就是在这些区域中执行较多的gc任务,以尽可能地减少jvm pause以及对用户的影响。虽然这些额外的工作是会对延时有较大的影响,不过从长远来看,它的jvm pause时间会更短,并可以提供更稳健的延时,而不是抖动的延时。

CMS

CMS是一个性能较好的GC算法,已被使用多年。启用的参数为:

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC

(这里其实在指定 -XX:+UseConcMarkSweepGC 后就默认已经指定了-XX:+UseParNewGC,这里显示指定为了之后进一步的讨论)

第一个选项是为Young Generation 设置使用Parallel New Collector:它在清理Young Generation 时会停止整个Java 进程。这个在Young Generation中是可以被接受的,因为它的内存相对Old Generation 会少得多,所以此过程也不会消耗太多的时间,一般少于几百毫秒。不过,这个设置对于Old Generation是不合适的,在最坏的情况下,可能会导致多达几秒的延时。

对此,CMS可以较好地解决此问题,它会尽可能地并行做gc,而不停止Java 进程。虽然此方法会造成额外的CPU资源消耗,但是可以减少或是避免JVM pause。CMS也是hbase 中默认的gc算法。

在CMS中,我们可以做的第一个调整是:Young Generation的大小。它默认的值较小,在region server 执行写操作时,会将数据写入memstore,过小的Young Generation会让这部分数据停留在此区域的时间较短,并可能造成不必要的generation 提升(Young Generation 提升到 Old Generation)。对此,可以将Young Generation的大小设置为一个固定的、更大的一个值,以减少此类影响,例如:

-XX:MaxNewSize=128m -XX:NewSize=128m

或者也可以直接用这两个配置的简写:-Xmn128m。

128MB是一个较好的起点,之后可以根据JVM的指标,确定是否满足需求或是是否需要添加更多的内存。不过对于有较高负载的region server 来说,默认的Young Generation 的大小有些过于小了。如果在一开始不调整此参数的话,我们可能会注意到CPU的使用率会偶尔突然增高,因为JVM需要花时间将Young Generation 中的对象提升到Old Generation中(由于Young Generation中内存不足)。

CMS还有一个参数用于控制:什么时候开始做concurrent mark and sweep 检查。这个参数可以通过以下配置设置:

-XX:CMSInitiatingOccupancyFraction=85

此参数指定的是:什么时候启动后台cms垃圾回收。此参数不能配置过高,为了避免concurrent mode failure 问题。此问题发生的场景为:在后台mark and sweep 工作正在进行时,堆内存已经耗尽了所有的可用空间。则此时,JRE必须停止Java 进程,并且通过清除垃圾,或是将Young Generation 中的对象移动到 Old Generation中(如果这些对象已经够old),以释放空间。

设置此值为85%,表示在堆内存耗尽之前就会开始执行concurrent collection 的工作,但也不会是太早去做这个工作。一个常规的建议是:将这部分值设置为稍高于memstore与block cache总和大小的值。它们的总和一般为80%(memstore 40%,block cache 40%),所以我们会优先设置为85%左右或以上。

下面是一个起始的CMS参考配置:

export HBASE_REGIONSERVER_OPTS="-Xms10g -Xmx10g -Xmn128m \
  -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 \
  -XX:+UseCMSInitiatingOccupancyOnly"

G1

G1的启用方法为:

-XX:+UseG1GC

除此之外,还需要设置一个预期最大的gc pause 时间。不过这个仅是一个soft goal,JVM会尽量满足此目标。不过若是为了满足工作负载,此暂停时间可能会更长。此参数为:

-XX:MaxGCPauseMillis=200

默认即为200ms,可以根据需求做进一步调整。最后一个参数需要调整的是:什么时候gc collector 开始一个concurrent GC cycle,以整个堆内存已使用部分百分比为阈值,默认为45%,例如:

-XX:InitiatingHeapOccupancyPercent=45

可以先以此为初始配置,然后再根据负载调整G1 中每个region的大小等,此部分需要更深入的G1 算法知识,之后再详述。

另一个可以用于参考的配置是 HubSpot 发布的一个一篇 HBase 中 G1 GC 调优文档:

https://product.hubspot.com/blog/g1gc-tuning-your-hbase-cluster

根据文档中的建议,我们可以尝试使用以下配置:

-XX:MaxGCPauseMillis=50

-XX:+UnlockExperimentalVMOptions

-XX:-OmitStackTraceInFastThrow

-XX:+ParallelRefProcEnabled

-XX:+PerfDisableSharedMem

-XX:-ResizePLAB

-XX:ParallelGCThreads=8 + (<no. of logical processors> - 8) * (5/8)

另一方面,根据另一篇 Intel 团队文档:

https://software.intel.com/en-us/blogs/2014/06/18/part-1-tuning-java-garbage-collection-for-hbase

对 Young Generation 的调优的建议为(默认 Young Generation 的比例为 5%):

32GB heap, -XX:G1NewSizePercent=3;

64GB heap, -XX:G1NewSizePercent=2;

100GB and above heap, -XX:G1NewSizePercent=1;

最终 Intel 给出的 region server JVM 的参数为:

-XX:+UseG1GC

-XX:+UseG1GC

-Xms100g -Xmx100g (Heap size used in our tests)

-XX:MaxGCPauseMillis=100 (Desired GC pause time in tests)

-XX:+ParallelRefProcEnabled

-XX:-ResizePLAB

-XX:ParallelGCThreads= 8+(40-8)(5/8)=28

-XX:G1NewSizePercent=1

针对这里的建议是:对 Young Generation,使用 Intel 的配置;对 RegionServer 其他的配置,采用 HubSpot 的配置(为2016年发布,Intel 对 RegionServer 的配置建议为 2014 年发布)。

HBase 中的 JVM 与 GC的更多相关文章

  1. JVM的GC概述

    JVM的GC概述 GC即垃圾回收,是指jvm用于释放那些不再使用的对象所占用的内存.在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能. 有些垃圾收集专用于特殊的应用程序.比如,实时应用程序 ...

  2. Linux使用jstat命令查看jvm的GC情况

    Linux使用jstat命令查看jvm的GC情况 http://www.open-open.com/lib/view/open1390916852007.html http://www.aiuxian ...

  3. JVM、GC与HashMap

    阿里巴巴突然来了个面试邀请电话,问了些java底层的东西,不知所措,所以专门花了些时间做了下学习,顺便记录下,好记性不如烂笔头. 一.对JAVA的垃圾回收机制(GC)的理解 不同于C/C++需要手工释 ...

  4. 初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为

    初步探究java中程序退出.GC垃圾回收时,socket tcp连接的行为 今天在项目开发中需要用到socket tcp连接相关(作为tcp客户端),在思考中发觉需要理清socket主动.被动关闭时发 ...

  5. 明白生产环境中的jvm参数

    明白生产环境中的jvm参数 写代码的时候,程序写完了,发到线上去运行,跑一段时间后,程序变慢了,cpu负载高了--一堆问题出来了,所以了解一下生产环境的机器上的jvm配置是有必要的.比如说: JDK版 ...

  6. spark批量写写数据到Hbase中(bulkload方式)

    1:为什么大批量数据集写入Hbase中,需要使用bulkload BulkLoad不会写WAL,也不会产生flush以及split. 如果我们大量调用PUT接口插入数据,可能会导致大量的GC操作.除了 ...

  7. Linux使用jstat命令查看jvm的GC情况(转)

    B. jstack jstack主要用来查看某个Java进程内的线程堆栈信息.语法格式如下: 1 jstack [option] pid 2 jstack [option] executable co ...

  8. 深入探究jvm之GC的参数调优

    在上一篇博客记录了GC的算法及种类,这篇博客主要记录一下GC的参数如何调整以提高jvm的性能. 一.堆的回顾: 堆的内存空间总体分为新生代和老年代,老年代存放的老年对象,新构造的对象分配在eden区中 ...

  9. jvm的GC日志分析 [转]

      jvm的GC日志分析 标签: jvm内存javagc 2015-06-22 16:37 1566人阅读 评论(1) 收藏 举报  分类: Java(4)  JVM的GC日志的主要参数包括如下几个: ...

  10. JVM的GC简介和实例

    本文是一次内部分享中总结了jvm gc的分类和一些实例, 内容是introduction级别的,供初学人士参考.成文仓促,难免有些错误,如果有大牛发现,请留言,我一定及时更正,谢谢!JVM内存布局主要 ...

随机推荐

  1. 使用 Data Assistant 快速创建测试数据集

    使用 Data Assistant 快速创建测试数据集 Data Assistant 提供超过 100 种数据类型,为任何开发.测试或演示目的生成大量.异构.真实的数据. 官网地址:http://ww ...

  2. Lua 学习笔记(自用)

    Lua 学习笔记 1 语言基础 运行方式类似Python,可以直接在交互行运行,也可以通过解释器运行某个脚本.也可以在交互行运行某个lua脚本 dofile("hello.lua" ...

  3. 游戏陪玩公众号H5软件开发方案图文详解

    用户需求 无论开发怎样的产品,都需要事先对整个市场行情和用户需求进行简单的了解.前面的一组数据已经简明扼要的摆明了现在陪玩市场的行情.而现如今,大多数游戏都需要组队进行,如英雄联盟.王者荣耀.绝地求生 ...

  4. 1分钟了解什么是SQL聚合函数,一看就懂,一学就会!(AVG(column_name)\COUNT(column_name)\MAX(column_name)\MIN(column_name)\SUM(column_name))

    聚集函数:SQL基本函数,聚集函数对一组值执行计算,并返回单个值,也被称为组函数.聚集函数经常与SELECT语句的GROUP BY子句的HAVING一同使用.但是不可用于WHERE语句中,因为WHER ...

  5. Web3连接以太网

    1. Infura Infura 是一种托管服务,提供对各种区块链网络的安全可靠访问,消除了管理区块链基础设施的复杂性,使开发者能够专注于构建创新的 Web3 应用程序. Infura 作为连接应用程 ...

  6. smtplib详解,发送邮件

    创建邮箱账号 1.官网登录邮箱. 2.在邮箱的主界面找到"设置",新版的主界面与旧版稍有不同,一般位于上方,齿轮状的即是. 3.点击齿轮状的设置标志,会弹出一个下拉菜单,在最后有我 ...

  7. ansible api调用及二次封装详解

    ansible 2.7调用 程序 import json from collections import namedtuple from ansible.parsing.dataloader impo ...

  8. php的下载与安装

    一,进入到php的官方网站   https://www.php.net/   ,进入到下载页面: 选择 PHP 7.2.30 的Windows版本 具体的下载页面版本区分: 下载后,得到如下压缩包到如 ...

  9. NOIP模拟62

    T1 Set 解题思路 抽屉原理 发现对于前缀和向 \(n\) 取模之后一定是右两个值相等的(包括什么都不选的 0 ). 假设 \(pre_j=pre_i\) 那么 \([j+1,i]\) 之间这一段 ...

  10. 微信小程序设置swiper圆角在ios上失效

    今天在给轮播图添加圆角的时候,发现在安卓机上是有圆角的,但是在苹果手机上圆角却失效了,后来翻阅了文档发现这是个官方的bug 解决方法1 border-radius: 20rpx; /*再设置个tran ...