HBase 是一个基于 HDFS 的低成本、分布式 LSM 结构数据库,可以支持毫秒级别查询;支持海量的 PB 级的大数据存储,适用于高 QPS 的随机读写和前缀范围查询等场景。此外,优秀的开源环境使得 HBase 还可以支持丰富的上下游生态与离线任务。
目前在滴滴内部,HBase 基本覆盖了全部业务线,数据量 PB 规模,吞吐超千万级别;业务包含司乘轨迹、订单、特征工程、推荐引擎、IOT、APM 等各种场景,基于 HBase 的多模生态诸如 OLAP(Kylin)、时序(OpenTSDB)、时空(GeoMesa)、图(JanusGraph)亦均有应用。
2020 年下半年,HBase 团队逐渐将视野投向端上/类端上业务,希望能够承载更加重要的流量。然而对于 HBase 自身架构和实现而言,主要存在两方面痛点:
▍1.可用性问题
架构层面看,HBase 在 CAP 定理中选择了 C,以较弱的可用性为代价换取强一致性,数据层面依赖 HDFS 保证数据安全,计算层面 region 无副本。
这样当 region 迁移、分裂、合并、RS 宕机等情况发生时,对应 region 都会有短时不可用;而作为高吞吐的数据服务,客户端往往都会大量使用线程池,少量 region 不可用会迅速形成木桶短板,进而放大为整体 TPS 掉底。
而这种“预期内的”抖动、掉底,是无法满足互联网行业端上场景的可用性要求的。
社区提供的 region replica 功能一定程度上可以缓解这一问题,但一方面目前这个 feature 可靠性还不算高,社区仍在推进各种加固和改善,目测稳定的目标 release 版本可能要放到未发布的 3.0 了;另一方面端上服务需要双机房,保证容灾和降级,而 replica 是集群内的 region 副本,显然也不能支持。
▍2.毛刺问题
HBase 主要受 Java GC 和底层 HDFS 共用影响,HBase 的毛刺相对突出,是进一步提升性能的瓶颈点。
基于以上两个痛点问题,HBase 团队近半年进行了一些尝试与探索,主要是基于 replication 的客户端多路读功能 与 HBase-ZGC 应用实践,预期能够优化 HBase 的可用性与毛刺问题,简单分享给大家。
2020 年来,为提升 HBase 可用性,我们大体经历了两个阶段:
1. replication 主备
replication 是 HBase 的异步数据同步机制,和 Mysql 利用 Binlog 实现主从库类似,HBase 利用 WAL 实现主备集群的数据同步。大致流程为主集群记录写入的 WAL,并将数据异步发送给备集群,备集群接收数据并将其转换为 put/delete 操作,批量写入备集群。提供最终一致性保证。
这一阶段存在的问题:
2. replication + failover
failover 是滴滴 HBase 团队基于 replication 自研的增强 feature,架构如下图:
▍1. 设计
整体设计参考 HDFS 的 hedgedRead 功能,客户端首先向主集群发起读请求,一定时间没有返回结果则并发向备集群发起请求,两者取先完成者返回。
实际上 HBase 的 regionReplica 也是类似的实现。
▍2.新增配置
param | default value | desc |
---|---|---|
hbase.client.hedged.read | false | 是否开启多路读 |
hbase.client.hedged.read.timeout | 50 | 启动备集群读线程的时间阈值 |
hbase.zookeeper.quorum.hedged.read | - | 备集群zk地址 |
▍3.性能测试
3.1 用例设计
3.2 测试结论
P99 对比多路读对于 max 和 P999 有较佳优化效果,可以有效打磨毛刺。
▍4.未尽事项和思考
1. 多路读功能基于 replication 实现,因此只能实现最终一致性,备集群读到的数据有可能和主集群存在差异;
2. 目前此功能仅作用于查询,主集群宕机时,最新数据无法同步,因此备集群查询最新数据可能查询不到;
3. HBase 的 scan 操作可能分解为多次 RPC,由于相关 session 信息在不同集群间没有同步,数据也不能保证完全一致,因此多路读只在第一次 RPC 时生效,之后的请求会固定访问第一次 RPC 时最终使用的集群。
多路读本质上是多活建设,但 CAP 较难跨越,多活可以提供高可用能力,但强一致性很难得到保障。
但我们可以通过“让用户选择”的方式来解决这一问题:
方案一:多活 + 最终一致性
方案二:主备 + 强一致性
对于方案一,当前的多路读实现了读链路的多活,写链路仍有优化空间,例如提升 replication 效率、降低两集群间数据 lag 等;对于方案二,可以基于社区的同步 replication 实现,此外 failover 的功能仍需我们做更多工作,实现更加智能的自动切换,降低用户感知。
随着滴滴内部越来越多的端上和类端上业务使用 HBase 作为存储引擎,用户对 HBase 读写延迟稳定性的要求也越来越高,HBase 的 GC 毛刺问题尤为突出,G1 无法满足性能需求。好消息是 JDK15 在 9 月 15 号正式发布,划时代的 ZGC 正式转正,我们决定尝试用 ZGC 来解决 GC 毛刺问题。
ZGC(The Z Garbage Collector):ZGC 是 JDK11 之后发布的一款可伸缩的低延迟 JVM 垃圾收集器。ZGC 官方的设计目标如下:Max pause times of a few milliseconds(*)Pause times do not increase with the heap or live-set size (*)Handle heaps ranging from a 8MB to 16TB in sizeZGC 是一个并发的、单代的、基于区域的、NUMA 感知的压缩收集器,Stop-the-world 阶段仅限于根扫描,因此 GC 暂停时间不会随堆或活动集(live set)的变大而增加。ZGC 的核心设计原则是将 Load barriers 与染色对象指针结合使用,这使得 ZGC 能够在 Java 应用程序线程运行时执行并发操作,例如对象重定向。从 Java 线程的角度来看,在 Java 对象中加载引用字段的行为受到加载屏障的影响。除了对象地址之外,colored oops 还包含加载屏障使用的信息,以确定在允许 Java 线程使用指针之前是否需要采取某些操作。ZGC 相比 G1 更低延迟:GC 停顿时间更短,不超过 10ms 更大内存:堆内存支持范围更大(8MB-16TB)SPECjbb 2015 基准测试,128G 堆,ZGC 的暂停时间远低于 G1
相较于 G1 只有写屏障没有读屏障,复制移动的过程需要 Stop the world,ZGC 通过读屏障、Remark 标记和重定向表来并发拷贝非 GC Roots 对象,尽可能的减少了 Stop the world。官方的性能测试对比可以参考 JEP333。本文主要介绍 HBase 场景的 ZGC 应用实践,对 ZGC 的原理不展开介绍。
2.1 选择 JDK 版本
由于需要在生产环境使用,而 Orace JDK 商业使用开始收费,所以我们需要一个免费版的 JDK。经过对比,我们最终选取了 AdoptOpenJDK 的 JDK15 版本。
1)AdoptOpenJDK 是社区(伦敦 JUG)维护版的 OpenJDK,提供预构建的二进制文件,主要维护 LTS 及最新版本;和 OpenJDK 一样,AdoptOpenJDK 也支持 GPL 协议且免费,不同的是 OpenJDK 只会由 Oracle 提供 6 个月的安全更新,而 AdoptOpenJDK 则由社区提供至少 4 年的免费长期支持(LTS)。
2)选择 JDK15 的原因:
JDK11 存在小概率 crash 的问题
ZGC 在 JDK15 版本正式生产环境可用
2.2 编译 HBase(基于 AdoptOpenJDK15)
滴滴内部使用的 HBase 版本是基于社区 1.4.8 基础上开发的版本,不支持 JDK11 及以上版本的编译,所以需要解决一些编译问题。
社区 3.x 及 2.3.x 版本开始支持 JDK11,参考 HBASE-22972。在 1.4.8 版本的编译过程中,主要遇到了以下几类编译问题:
1. 部分类找不到或被删除,比如 javax.xml.ws.http.HTTPException。解决办法:找到替换类或依赖包;
2. 一些类不可读,比如 sun.nio.ch.DirectBuffer。解决办法:运行时添加--add-exports=java.base/sun.nio.ch=ALL-UNNAMED;
3. 依赖的组件包不支持 JDK15,如 Jetty、Jruby 等。解决办法:升级对应的组件到高版本;
4. 编译插件不支持 JDK15,如 maven-shade-plugin、extra-enforcer-rules 等。解决办法:升级对应的插件到高版本。
2.3 应用 ZGC 的效果:
1、顺序读场景 ZGC 和 G1 性能表现对比
Scan 场景下,ZGC 的 P99 延迟降低 20%,P999 降低 40%。
2、压力测试场景对比 ZGC 和 G1 的性能表现
构造写压力测试场景,RegionServer 的全局 Memstore 写满,触发 Upper Limit,G1GC 回收不过来,触发 Full GC,耗时超过 40s(ZK 会话的超时时间),RS 服务会宕机。
对比同等写入压力下,ZGC 99.93%的回收时间都在 10ms 以内,只有 2 次在 10~20ms 之间,RS 服务未宕机。
备注:如果内存分配过快,ZGC 也可能会出现回收不过来的问题,这种情况下可以通过增大堆内存的方式缓解。
HBase 使用 ZGC 可以有效的降低了服务端 P99 及 P999 的延时,非常适合对延迟较敏感的业务场景。
HBase 在真正海量数据的离线应用场景下具备毋庸置疑的竞争力,但受其自身实现短板的限制,距离端上应用的标准还是存在一定距离的。滴滴 HBase 希望通过一系列优化手段,服务好离线业务的同时,未来可以接入更多的不涉及核心流程的线上/类线上业务,欢迎感兴趣的同学一起交流。
领取专属 10元无门槛券
私享最新 技术干货