新版Hive 3.0 Metrics系统会引发CPU飙高。
1 为什么要做这件事?
我们公司内部使用的Hive为开源社区的1.2.1版本,当前Hive社区已经推出了3.0版本。但由于Hive不同版本之间的HQL兼容性问题,直接升级至高版本会有很高的成本代价。因此,我们做了两件事:一是将Hive迁移至Spark任务(Hive SQL迁移Spark SQL在滴滴的实践),从而降低Hive在离线计算中的比重;二是启动了元数据服务优化专项,确保Hive服务的稳定性。第一件事我们去年已经完成了国内集群的任务迁移,今年将陆续完成美东,俄罗斯,欧洲等集群的任务迁移。第二件事我们去年完成了元数据服务的Mysql迁移至RDS,今年将重点从指标监控,元数据迁移,任务分级保障等角度进行为期一年的优化专项。本文描述的便是我们在引入社区指标监控时触发的问题。
为了更好地掌控Hive Metastore(下面简称HMS)服务,保障服务稳定性,调研后我们将社区最新版本的指标监控相关代码backport到了我们内部自研版本。然而上线后我们发现CPU飙高至90%以上,于是做了回滚。
2 需要做什么?
指标监控需要做两件事: 一是指标的采集能力;二是指标的服务能力。
在充分调研社区方案之后,我们决定将社区最新版Hive3.0中的Metrics体系引入到我们内部版本中来,从而实现指标的埋点采集功能;另外我们还开发了JmxHttpServer,来暴露一个http服务提供指标监控大盘系统的数据服务能力。这两点均不难实现。
指标采集相关入口在: org.apache.hadoop.hive.metastore.metrics.Metrics
http服务参考HS2的web服务实现。
3 出现了什么问题?
我们在合入社区代码后,进行了充分的功能验证,但并未怀疑过社区代码可能存在的性能问题,从而忽略了整个服务的性能压测。在上线后我们发现了CPU持续飙高,另外HMS在启动期间耗时较长。在保留现场后,我们立马进行了回滚操作。
最终通过采集火焰兔我们确认CPU飙高是社区代码存在性能问题(关于CPU高如何排查篇幅原因我将另起一文),代码如下:
1 | "pool-3-thread-8002" #15828 prio=5 os_prio=0 tid=0x00007f0d92f02000 nid=0x20a01 runnable [0x00007f02a71f4000] |
TreeMap的添加总是要触发排序,因此在高qps场景下,极易出现性能问题。
这里我们一起读一下Metrics包中的源码实现,发现每次rpc调用都要走到下面代码,每次都要创建一个新的TreeMap对象并循环添加元素,从而定位到问题根因。
1 | @SuppressWarnings("unchecked") |
而启动耗时较长的原因是:启动时需要初始化库表分区水位监测的三个指标值,由于我们的分区总量在Mysql单表中已经高达8亿的数据量,所以查询count(*)时耗时较长。这个解决方案较为简单,我们改为异步初始化即可。耗时源码如下:
1 | @VisibleForTesting |
4 如何修复?
我们看上层调用栈中的 getOrCreateCounter 方法,无非就是想从Map中获取指定key的Counter,而HMS总是先获取所有,这显然是没有必要的:源码如下:
1 | public static Counter getOrCreateCounter(String name) { |
查看counter方法我们发现底层是 getOrAdd,也就是说我们可以直接调用这个方法从而获取指定的Counter即可。
1 |
|
这样不会有问题吗?社区这么做不对吗?我们说社区代码其实是严谨的,但确实带来了性能问题。我们直接调用counter方法,确实存在多线程的并发问题,但这个并发问题只存在于第一次新建;而且对于一个指标大盘采集系统而言,一两个数据采集的丢失是可以忽略不计的。因而,改动如下:
1 | public static Timer getOrCreateTimer(String name) { |
库表分区水位指标的初始化,修改为异步:
1 | @VisibleForTesting |
5 总结
首先,问题的隐蔽性。性能问题只有在高qps下才会凸显出来。这应该也是社区为什么没有发现这个问题的原因。启动慢的问题是由于我们业务体量大(mysql单表存储数据量在8亿的量级),而在业界元数据服务中,大多数mysql存储都在百万量级,因而不存在查询心更难问题。
对于开源社区代码依然要保持怀疑的态度,在CPU飙高排查的一开始,就已经注意到了这个栈信息,但由于是社区代码没有过多地猜疑,导致走了很多弯路。CPU高的问题排查是个难点,后面我会单独分享一下。另外这个问题的修复,后续也会提交给社区。
本文链接: https://stefanxiepj.github.io/archives/1404c17c.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!