目录
引言
HBase是基于Hadoop构建的一个高可用、高性能、支持水平扩展的NoSQL数据库,因其优秀的表现能力,多用在实时或准实时场景。在货拉拉,HBase承担了在线存储的职责,支持着风控、地图、实时标签等重要业务场景。
同时,在实际生产环境中,存在大量数据T+1
的场景:即用户需要每日基于Hive中各系统的各类数据生成特定业务数据(如标签、指标等),定期将这部分数据(全量或增量)导入HBase系统,以提供在线服务查询,支撑风控、运营等场景。
问题与挑战
在初期,由于缺乏统一的离线数据导入流程,业务基于HBase Bulkload实现各自的Hive to HBase代码,在线上实践中,存在一些痛点与稳定性的风险,总结如下:
-
1. 开发效率低下重复开发,部分业务开发不熟悉大数据组件技术,耗时又耗力。
-
2. 缺少链路保障:部分业务对数据产出时间有较大依赖,对任务失败或延期、数据丢失等问题缺少发现能力。
-
3. 影响HBase稳定性:高峰期执行Bulkload造成多次线上隐患,甚至出现过用户将A集群HFile Load到B集群(由于B集群到A集群的NN端口不通),导致线上HBase 20min不可用。
因此,需要构建一套Hive To HBase的通用工具,并且保障整个数据链路的稳定性,主要需求点:
-
1. 简单通用:能满足绝大多数业务的数据转化需求,集成到数据开发平台,用户只需简单参数配置即可。
-
2. 可观测,可告警:能明确输入输出数据量,任务执行时间管理等。在任务异常(延时、报错等)时能主动通知到任务负责人,重要业务场景需执行对应的应急预案。
-
3. 可管控:依托数据开发平台,对任务变更、上线全流程增加审批管控,减少变更带来的稳定性风险。
调研
Hive To HBase的实现通常有两种架构,流程分别如下。
-
• 方案一
-
1. 在离线集群(YARN/HDFS/Hive混部集群)利用Spark/MR读取Hive数据转换成HBase内部数据格式,并直接将HFile写到在线HBase集群上。
-
2. 通过HBase内置工具LoadIncrementalHFile将生成的HFile加载到HBase集群中。
优缺点:
-
• 方案二
-
1. Transform阶段将HFile写到离线集群中。
-
2. 引入Hadoop DistCp工具,依赖其灵活的限速配置,将生成好的HFile拷贝到在线HBase集群中。
结论:由于不限速对在线HBase存在较大稳定性隐患,最终使用了方案二来实现。
实践过程
Transform
关于数据处理的实现逻辑,社区中有较多优秀的实践,这里就不再展开。在货拉拉的实际生产中,大部分业务的需求场景比较简单,使用统一的Transform流程即可,但也存在由于业务特性,需要定制化实现的场景,本次仅介绍统一方案。为了便于使用,程序封装在脚本中,集成在数据开发平台作为模版任务,用户仅需配置几个简单参数即可创建任务,实现数据处理,方便快捷。
脚本具备如下特性:
-
• 多RowKey生成策略:自定义RowKey设计,例如Hash,加盐和截取字段。
-
• 列名映射:支持HBase表列名与Hive列名的自定义映射,不需要强求按照hive表的字段名字或顺序。
任务上线后,在生产环境灰度时,也发现了如下问题:
-
1. 触发Compact高峰,抢占资源:风控业务每次为单个Region生成多个HFile(单个文件比较小),在CompactionChecker的下次检测时,会触发Compact高峰大量占用CPU和IO资源,并且这个数据导入任务依赖公司离线报表,离线报表产出发生延时后,Bulkload操作就可能在业务高峰期才被执行,对HBase在线服务存在较大的稳定性风险。
-
2. 数据本地率低,查询P99上涨:Distcp随机选择DN节点进行拷贝,这会导致HBase表的本地率下降,对于延时较为敏感的业务场景,是不可接受的。
针对上述两个问题,结合业务特点的分析,提出整体的解决方案:
-
1. 业务合并任务,减少为一个Region生成的HFile数量。
-
2. 优化表级Compaction配置,尽量在Bulkload后不触发Compaction(可以在Transform过程中设置hbase.mapreduce.hfileoutputformat.compaction.exclude 来避免)。
-
3. 部分业务不能设置TTL,此时Bulkload的文件无法被Minor Compaction清理,为避免单个Region下文件数不断增加,通过一个外挂的Major Compaction工具,在业务低峰期调起Major Compaction来合并HFile。
-
4. 改造Distcp,支持设置favoredNodes,提高数据本地率。
Compaction工具
在进行了HFile数优化和Compact配置的调整后,业务在Bulkload的查询抖动得到了明显的改善:
优化前
优化后
但由于业务特性,这部分业务表不能设置TTL,所以只能通过Major Compact来清理数据与降低HFile数。为了避免在高峰期执行Major Compact,关闭了默认的Major Compact,并且实现了一套Major Compact工具,支持多种调度策略:
-
1. OffpeakCompact:用来控制低峰期才提交Compact任务,具体的Region选举策略为如下几个。
-
2. TimeCompact:基于时间间隔的策略,计算Region上一次Major Compact与当前时间之差。
-
3. FileNumberCompact:计算Region的Store中的HFile数是否超过配置值。
效果
上线运行了一段时间,观察合理控制了表的HFile数量,同时规避了Bulkload后的Compact高峰。
但也在实际的运行中,发现一些问题:
-
1. 存在一些Region的lastMajorCompactionAge始终为0,这就导致使用TimeCompact策略时是失效的。通过分析,发现Bukload的HFile文件中的create_time_ts为0 。
再结合代码分析,发现HFileOutputFormat2在构建HFile时未设置CreateTime,代码如下:
//1.生成HFile时创建writer
HFileOutputFormat2.getNewWriter()
HFileContextBuilder contextBuilder = new HFileContextBuilder()
.withCompression(compression)
.withChecksumType(HStore.getChecksumType(conf))
.withBytesPerCheckSum(HStore.getBytesPerChecksum(conf))
.withBlockSize(blockSize);
//2.load HFile时split HFile
LoadIncrementalHFiles.copyHFileHalf()
HFileContext hFileContext = new HFileContextBuilder().withCompression(compression)
.withChecksumType(HStore.getChecksumType(conf))
.withBytesPerCheckSum(HStore.getBytesPerChecksum(conf)).withBlockSize(blocksize)
.withDataBlockEncoding(familyDescriptor.getDataBlockEncoding()).withIncludesTags(true)
.build();
halfWriter = new StoreFileWriter.Builder(conf, cacheConf, fs).withFilePath(outFile)
.withBloomType(bloomFilterType).withFileContext(hFileContext).build();
优化了相关代码后,整体运行结果符合预期,同时也将这部分回馈到了社区,相关JIRA:HBASE-27636,HBASE-27688,PHOENIX-6955
-
2. 尽管配置了Off-peak Compactions,但在业务低峰期依旧不能充分利用集群资源去合并HFile。通过分析,发现目前的Off-peak Compactions是全局比较,同一时间只能运行一个Off-peak的Compact。关键代码如下:
private static final AtomicBoolean offPeakCompactionTracker = new AtomicBoolean();
// Normal case - coprocessor is not overriding file selection.
if (!compaction.hasSelection()) {
boolean isUserCompaction = priority == Store.PRIORITY_USER;
boolean mayUseOffPeak =
offPeakHours.isOffPeakHour() && offPeakCompactionTracker.compareAndSet(false, true);
try {
compaction.select(this.filesCompacting, isUserCompaction, mayUseOffPeak,
forceMajor && filesCompacting.isEmpty());
} catch (IOException e) {
if (mayUseOffPeak) {
offPeakCompactionTracker.set(false);
}
throw e;
}
assert compaction.hasSelection();
if (mayUseOffPeak && !compaction.getRequest().isOffPeak()) {
// Compaction policy doesn't want to take advantage of off-peak.
offPeakCompactionTracker.set(false);
}
针对此处的优化,我们正在内部进行相关的优化验证,也创建了相关JIRA:HBASE-27861,希望能与社区一起讨论。
DistCp
对于延时要求较高的业务,数据本地率对其RT的P99
、P999
影响较大,尽管HBase在 HBASE-12596 中优化了Bulkload的数据本地率,但使用DistCp后,数据本地率则无法保障。对此针对DistCp工具进行改造并且封装成新的工具,具备如下特性:
-
1. 指定FavoredNodes:支持为不同的HFile文件设置特定的FavoredNodes。
-
2. 多集群拷贝:用户只需简单配置目标集群名,并可完成同份数据拷贝到集群。
对于DistCp的改造,部分能力已开源,参见 HADOOP-18629。同时也针对HBase进行两项对应的优化,见:HBASE-27670,HBASE-27733。
数据校验
部分业务非常在意Bulkload后数据的质量,有多少数据成功落HBase,是否存在丢数等。为解决用户的担忧,保证数据的正确性和可靠性,主要进行了如下验证:
-
1. Bulkload流程验证,通过如下指标判断完成度
-
• Transform:实现Counter计算行数。
-
• Distcp:拷贝前后HFile目录总大小。
-
• Load:HFile文件是否被清空,表级Bulkload的相关监控。
-
-
2. 抽样验数,用户指定RowKey查询HBase表。
稳定性保障
数据链路作为服务的延伸,在整体的稳定性保障中也相当重要,对此我们也构建了对应的链路稳定性保障方案(方案如下),从而提升整体的稳定性,目前能力基本已落地或正在落地。
总结
本文从用户痛点与服务稳定性的角度出发,介绍了货拉拉大数据基础架构团队在HBase离线数据链路保障上的思考与实践。希望能为读者提供一些参考,也欢迎与我们沟通交流。