文章目录
ZGC初体验——OpenJdk 15编译HBase 1.4.8
1. 前言
我们线上HBase集群的GC方式早已切换至G1,而且对G1的各个参数也做了详细的压测,并对比实验结果,搭配出了一组较为优秀的参数配置,再结合我们接口级别的主备熔断机制,在过去很长的一段时间里,我们集群的超时率一直控制在很低的范围之内,优于业务方的预期。
但随着业务方对HBase集群查询延时的要求越来越高,线上集群单纯getRow的查询响应已无法到达其满意的三个9。一系列的测试之后我们得出结论,GC毛刺导致查询超时毛刺,这里不过多描述测试的细节,后续会继续发文介绍GC与HBase的爱恨纠葛。
在我们可以完全排除网络抖动、硬盘延迟等方面的因素影响之后,GC的瓶颈就是拖慢HBase性能的最大掣肘。关于GC更深层次的理论知识,奈何我也在找书看,目前只能寥寥数字,记录一些粗浅的理解。
我们配置HBase的时候,会重点对RegionServer的JVM参数进行调优,会给它分配一个合适的内存大小,会为它设置一些GC参数,会调整读缓存和写缓存占用整个RS内存的比例,以上种种操作都是为了最大程度地把发挥出集群的性能。
一条HBase的查询请求从客户端达到服务端之后,会首先经过读缓存,缓存查不到数据才会去到磁盘中寻址。读缓存的机制是把最有可能被访问到的数据加载到内存里,而把一些不太可能被读到的数据所占的内存空间释放掉,这些无用对象的回收就需要GC的参与,合理的GC方式和合适的GC参数,会帮助我们更快、更及时地回收掉无用的对象,释放内存空间,进而保证整个服务高效而又稳定地运行。
以下内容将记录G1与ZGC的简单对比,将着重记录我用AdoptOpenJDK15
重编译社区版hbase-1.4.8
,并在IDEA中DEBUG调试和编译成最终安装包的过程,且在后续计划中,会对比G1,测试ZGC在HBase中表现究竟有多优秀。
2. G1
G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。
初识JVM,初始GC,让我感触最深的一个词是,STW,stop the world
,意指,当GC发生的时候,整个世界都将停止,那是否也意味着你整个系统中的所有application都将在此刻停顿,为GC让步?因此,从JDK3(1.3)开始,HotSpot团队一直努力朝着高效收集、减少停顿(STW: Stop The World)的方向努力,也贡献了从串行到CMS乃至最新的G1在内的一系列优秀的垃圾收集器,而ZGC则是比G1更优秀的GC算法。
3. ZGC
直接查看ZGC的目标吧。
- 低延迟,保证最大停顿时间在几毫秒之内,不管你堆多大或者存活的对象有多少
- 可以处理 8MB-16TB 的堆
暂时触及到了知识盲区,就不误人子弟了,还是直接上链接吧!https://www.zhihu.com/question/287945354/answer/458761494
4. 用AdoptOpenJDK15重编译hbase-1.4.8
4.1 JDK15在滴滴HBase上的应用
重编译hbase-1.4.8的想法来源于滴滴一篇关于HBase性能优化的文章。《滴滴在HBase性能与可用性上的探索与实践》
文章中介绍了ZGC的性能测评,以及他们团队内部的HBase对于ZGC的需求与实践,以及一些HBase方面的其他性能优化手段。
摘取jdk15编译HBase的简单流程,以此作为参考,也开始我们的编译之路。
这里我也选择hbase-1.4.8的源码进行修改和编译,当然,理论上更高版本的hbase代码与JDK15的兼容性应该更好,编译起来也更轻松,既然滴滴选择了1.4.8,那就说明1.4.8的编译是可操作的。
4.2 准备工作
- 社区版
hbase-1.4.8
源码包一份,请移步至官网下载 - AdoptOpenJDK15 请自行搜索该JDK的下载页面,下载对应操作系统的安装包
- maven-3.5.0 别的版本没有试过,不知道是否有坑
- IDEA-2020.3 付费版更好,社区版也够用,下载最新版的IDEA,主要为了可以在上面选jdk15
- Mac OS,其他操作系统没有尝试,估计坑会更多
附件
maven配置文件所需配置,加速依赖下载
<mirrors>
<mirror>
<id>aliyun</id>
<mirrorOf>*,!cloudera</mirrorOf>
<name>Nexus Release Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
<mirror>
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>https://repo1.maven.org/maven2/</url>
<mirrorOf>central</mirrorOf>
</mirror>
<!-- 中央仓库在中国的镜像 -->
<mirror>
<id>maven.net.cn</id>
<name>oneof the central mirrors in china</name>
<url>http://maven.net.cn/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
4.3 项目配置
用IDEA打开hbase源码的根路径,导入成maven项目。
配置你的JDK15
配置为每一个hbase项目的模块让其加载jdk15
当然,上述操作你都可以手动进行操作,但是,一旦你更新项目的pom文件,上述手动进行设置的配置,都会恢复原样。所以,为了避免每次都需要手动配置一次,我们来修改下pom文件中mavenn-compiler-plugin
下的配置。
<!--<maven.compiler.version>3.6.1</maven.compiler.version>-->
<!--提高下该插件的版本-->
<maven.compiler.version>3.8.1</maven.compiler.version>
<!--<compileSource>1.7</compileSource>-->
<!--compileSource修改为15,原来的1.7是指编译该版本JDK的最小版本是jdk1.7-->
<compileSource>15</compileSource>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${compileSource}</source>
<target>${compileSource}</target>
<showWarnings>true</showWarnings>
<showDeprecation>false</showDeprecation>
<compilerArgs>
<arg>--add-exports=java.base/jdk.internal.access=ALL-UNNAMED</arg>
<arg>--add-exports=java.base/jdk.internal=ALL-UNNAMED</arg>
<arg>--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED</arg>
<arg>--add-exports=java.base/sun.security.pkcs=ALL-UNNAMED</arg>
<arg>--add-exports=java.base/sun.nio.ch=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
<!--compilerArgs 下文细说-->
上述修改之后,重新加载下pom文件,上图中idea中java编译各个模块的jdk版本,会自动变为15,且compilerArgs标签中增加的编译时参数,也会自动传给idea。见下图
4.4 处理sun.misc.Unsafe not found异常
上述项目配置完成之后,尝试运行如下打包命令,或点下IDEA中工具栏中绿色的锤子,编译下项目。
# 在项目的根目录下执行
cd /Users/mac/other_project/apache/hbase/hbase-1.4.8
mvn clean package -DskipTests
不出意外的话,你一定会遇到这个异常。
org.apache.hadoop.hbase.util.Bytes
org.apache.hadoop.hbase.util.UnsafeAccess
Bytes和UnsafeAccess两个类中的sun.misc.Unsafe
替换为jdk.internal.misc.Unsafe
重新执行编译后,继续报错
这个错误就需要用到maven-compiler-plugin中的compilerArgs配置参数了,参考链接:JDK12 JDK.INTERNAL 包 不可见
https://www.freesion.com/article/992163055/
4.5 替换javax.xml.ws.http.HTTPException
找不到javax.xml.ws.http.HTTPException,需要找到替代类
项目根pom文件中加入如下依赖,注意加在dependencies标签下面
<dependencies>
<dependency>
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
<version>2.3.3</version>
</dependency>
</dependencies>
4.6 程序包javax.annotation不存在
项目根pom文件中加入如下依赖,注意加在dependencies标签下面
<dependencies>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
4.7 程序包com.sun.javadoc不存在
systemPath 请指向你的JAVA_HOME,jdk1.8也行
<dependencies>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.7</version>
<scope>system</scope>
<systemPath>/Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home/lib/tools.jar</systemPath>
</dependency>
</dependencies>
4.8升级jetty
hbase-server
、hbase-thrift
、hbase-rest
三个模块依赖jetty来编译JSP等web组件,项目中原有jetty为org.mortbay.jetty,而并非现在主流的org.eclipse.jetty,所以需要把原来jetty的版本升级为eclipse的。jetty不升级直接报如下异常,实在无法在原有jetty版本上解决这个错误。
错误日志大概是:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.6:run (generate) on project hbase-server: An Ant BuildException has occured: java.util.MissingResourceException: Can't find com.sun.common.util.logging.LogStrings bundle from -> [Help 1]
而滴滴原文中只是简单介绍了需要升级jetty,并未交代具体实现细节。没办法,只能灵机一动,直接参考hbase-2.2.3
中的jetty依赖。社区3.x及2.3.x版本开始支持JDK11,那么这俩大版本中的代码实现应该是最接近jdk15的。所以直接参考hbase-2.2.3中,这三个模块中jetty的依赖。
在根pom中增加高版本jetty组件,所做的改动如下:
<!-- <jetty.version>6.1.26</jetty.version>
<jetty.jspapi.version>6.1.14</jetty.jspapi.version>-->
<jetty.version>9.3.28.v20191105</jetty.version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util-ajax</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<!--This lib has JspC in it. Needed precompiling jsps in hbase-rest, etc.-->
<!-- 这个很重要,用来编译jsp的-->
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
在hbase-server模块中,对pom所做改动如下:
<!--加入新jetty依赖-->
<dependency>
<!--For JspC used in ant task-->
<groupId>org.glassfish.web</groupId>
<artifactId>javax.servlet.jsp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
<!--注释旧依赖-->
<!-- <dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-util</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-sslengine</artifactId>
</dependency>-->
<!--<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp-2.1</artifactId>
<scope>compile</scope>
</dependency>-->
<!-- <dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-api-2.1</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>servlet-api-2.5</artifactId>
</dependency>-->
剩余其他模块操作类似。
备注,maven-antrun-plugin这个模块就是用来编译JSP、自动生成java jsp代码等WEB资源的,是个大坑,但是捋顺了就踩过去了。
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<!-- Generate web app sources -->
<execution>
<id>generate</id>
<phase>generate-sources</phase>
<configuration>
<target>
<property name="build.webapps" location="${project.build.directory}/hbase-webapps"/>
<property name="src.webapps" location="${basedir}/src/main/resources/hbase-webapps"/>
<property name="generated.sources" location="${project.build.directory}/generated-sources"/>
<mkdir dir="${build.webapps}"/>
<copy todir="${build.webapps}">
<fileset dir="${src.webapps}">
<exclude name="**/*.jsp"/>
<exclude name="**/.*"/>
<exclude name="**/*~"/>
</fileset>
</copy>
<!--The compile.classpath is passed in by maven -->
<taskdef classname="org.apache.jasper.JspC" name="jspcompiler" classpathref="maven.compile.classpath"/>
<mkdir dir="${build.webapps}/master/WEB-INF"/>
<jspcompiler uriroot="${src.webapps}/master" outputdir="${generated.sources}/java" package="org.apache.hadoop.hbase.generated.master" webxml="${build.webapps}/master/WEB-INF/web.xml"/>
<mkdir dir="${build.webapps}/regionserver/WEB-INF"/>
<jspcompiler uriroot="${src.webapps}/regionserver" outputdir="${generated.sources}/java" package="org.apache.hadoop.hbase.generated.regionserver" webxml="${build.webapps}/regionserver/WEB-INF/web.xml"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
上述依赖修改完成之后,重新编译,个别类会出现报错,需要修改两处源码,但是代码修改也很简单,这里就不赘述了。最终,编译顺利通过,web代码以及静态资源可以正常生成。
4.9 提升一些maven插件的版本
maven-shade-plugin、extra-enforcer-rules 升级至新版,以应对一些莫名其妙的错误。
4.10 Jruby组件版本升级
Jruby 编译组件的版本升级至高版本,影响的模块也只有hbase-shell
,升级之后需要修改些rb代码,也很简单,思路还是直接抄hbase-2.2.3
源码。
<!-- <jruby.version>1.6.8</jruby.version>-->
<jruby.version>9.1.17.0</jruby.version>
4.11 编译时jdk忘记切换至jdk15
忘记切换jdk15就运行mvn编译命令的报错如下:
# -X编译参数打印DEBUG日志
mvn clean package -DskipTests -X
切换jdk15。
4.12 license受检异常
编译过程中会报license检查异常,需要配置编译时跳过license检查。
<execution>
<id>check-aggregate-license</id>
<!-- must check after LICENSE is built at 'generate-resources' -->
<phase>process-resources</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<evaluateBeanshell>
<condition>
File license = new File("${license.aggregate.path}");
// Beanshell does not support try-with-resources,
// so we must close this scanner manually
Scanner scanner = new Scanner(license);
while (scanner.hasNextLine()) {
if (scanner.nextLine().startsWith("ERROR:")) {
scanner.close();
return false;
}
}
scanner.close();
return true;
</condition>
<message>
License errors detected, for more detail find ERROR in
${license.aggregate.path}
</message>
</evaluateBeanshell>
</rules>
<!-- 直接设置skip为true <skip>${skip.license.check}</skip>-->
<skip>true</skip>
</configuration>
</execution>
至此,源码正式编译打包前的修改工作已经完成。
5. 为HBase源码打补丁
这里也顺势记录下为HBase源码打补丁吧,我们在使用HBase的过程中,可能会发现一些BUG,并修改源码,经过社区大佬审核和确认之后,就可以生成一个patch文件了,patch文件其实就是git的一些文件增删记录,打完patch之后,对源码的修改会自动体验在原来的代码上,主要是为了避免你自己手动复制粘贴后再出BUG。
这里也为hbase-1.4.8打一个滴滴大佬提供的patch,该patch具体修复的功能是,HBASE-22620 修复replication znode积压问题;
当然你也可以选择你感兴趣的patch。
参考链接
搜索HBASE-22620
下载对应的patch文件,放置hbase源码根目录,大致看一下patch文件的描述内容:
---
.../regionserver/ReplicationSource.java | 49 ++++++-
.../ReplicationSourceWALReaderThread.java | 5 +
.../hbase/replication/TestReplicationSource.java | 143 +++++++++++++++++++--
3 files changed, 180 insertions(+), 17 deletions(-)
主要对上述三个文件进行了修改。我们只关注ReplicationSource.java
# 进入项目的根路径下
# 检查patch/diff是否能正常打入
# 这一步相当重要,因为你下载的patch可能并不兼容你当前所使用的版本,如果不检查就打patch,可能会污染你的源代码
# 进入项目根目录,运行
git apply --check HBASE-22620.branch-1.4.001.patch
# 无输出表示可以继续向下执行,有输出,请看具体的输出信息
# 打入patch
git apply HBASE-22620.branch-1.4.001.patch
patch打入后,ReplicationSource.java行数增加了。你还可以查看具体修改了什么样的逻辑,patch打完后,重新编译源码。
6. IDEA中DEBUG调试源码
关于IDEA中搭建HBase的源码阅读环境,以及DEBUG调试一些代码,公众号中分享过两篇文章,且完全可操作。这里只记录一些不一样的地方,尤其是运行时application的一些配置项。
把conf目录移动到这两个模块文件夹中,并设置为源码包,如图显示蓝色。
新增Application HMaster
- 新增一个Application
- 设置名称,如:HMaster
- 设置Java版本
- 设置运行模块,hbase-server
- 设置启动的JVM参数
- 设置运行主类
- 设置main函数启动参数
- 设置working directory 你自己的项目路径
这里具体说明运行时的JVM参数:
-Dhbase.home.dir=/Users/mac/other_project/apache/hbase/hbase-1.4.8
-Dhbase.id.str=root
-Dlog4j.configuration=file:///Users/mac/other_project/apache/hbase/hbase-1.4.8/conf/log4j.properties
-Dhbase.log.dir=/Users/mac/other_project/apache/hbase/hbase-1.4.8/logs
-Dhbase.log.file=hbase-root-master.log
-Dhbase.root.logger=INFO,console,DRFA
--add-exports=java.base/jdk.internal.access=ALL-UNNAMED
--add-exports=java.base/jdk.internal=ALL-UNNAMED
--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED
--add-exports=java.base/sun.security.pkcs=ALL-UNNAMED
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED
注意有些文件路径修改成你自己的目录地址。
新增Application hbase shell
重点关注JVM参数,否则hbase shell无法运行。
-Dhbase.ruby.sources=/Users/mac/other_project/apache/hbase/hbase-1.4.8/hbase-shell/src/main/ruby
--add-exports=java.base/jdk.internal.access=ALL-UNNAMED
--add-exports=java.base/jdk.internal=ALL-UNNAMED
--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED
--add-exports=java.base/sun.security.pkcs=ALL-UNNAMED
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED
--add-opens
java.base/jdk.internal.misc=ALL-UNNAMED
main函数传参:
-X+O /Users/mac/other_project/apache/hbase/hbase-1.4.8/bin/hirb.rb
注意上述配置参数中的目录地址换成你自己的。
分别启动两个Application
在hbase-server/conf/hbase-site.xml配置文件中新增如下配置:
<configuration>
<property>
# 此配置是为了跳过版本检查
<name>hbase.defaults.for.version.skip</name>
<value>true</value>
</property>
<property>
<name>hbase.rootdir</name>
<value>file:///Users/mac/other_project/apache/hbase/hbase-1.4.8/hbase-data/hbase</value>
<description>hbase本地数据地址</description>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/Users/mac/other_project/apache/hbase/hbase-1.4.8/hbase-data/zookeeper-data</value>
<description>hbase 内置zk数据目录地址</description>
</property>
</configuration>
启动HMaster Application
HMaster正常启动无报错
启动hbase shell
Hbase shell正常启动无报错,且命令试用成功
运行测试用例
public class TestQuery {
@Test
public void testHBase() {
Configuration conf = HBaseConfiguration.create();
try {
Connection connection = ConnectionFactory.createConnection(conf);
Admin admin = connection.getAdmin();
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf("leo_test"));
HColumnDescriptor columnDescriptor = new HColumnDescriptor(Bytes.toBytes("info"));
tableDescriptor.addFamily(columnDescriptor);
//admin.createTable(tableDescriptor);
final List<String> tables = Arrays.stream(admin.listTableNames()).map(TableName::getNameAsString).collect(Collectors.toList());
System.out.println(tables);
Table table = connection.getTable(TableName.valueOf("leo_test"));
Put put = new Put(Bytes.toBytes("10001"));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("leo"));
table.put(put);
Get get = new Get(Bytes.toBytes("10001"));
Result result = table.get(get);
byte[] value = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name"));
System.out.println(Bytes.toString(value).toString());
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行时注意Application的配置
注意JVM参数
7. 最终打tar.gz包
mvn clean package -DskipTests -Dhadoop-two.version=2.7.4 assembly:single
当你看到绿油油的一片时,就证明你成功了。
tar.gz包就在这里
还是那个熟悉的目录:
8. 总结
至此,JDK15编译hbase-1.4.8
源码的整个流程,便记录完毕,其在idea中进行DEBUG调试的关键步骤也一并记录在内,方便之后的代码阅读、功能调试、甚至是源码修改。整个过程中可能还有些细枝末节没有阐述到位,但应该很简单就能解决掉。
HBase源码复杂而宏大, 学习之路漫长而遥远,了解JVM的底层,熟悉GC的优化,必定是无法绕过的门槛。后续会在测试集群中部署该版本的安装包,对比G1,解开ZGC的神秘面纱,探测HBase更巅峰的性能瓶颈。