过去的一年里,咱们准备在Ali-HBase上突破这个被广泛认知的痛点,为此进行了深度分析及全面创新的工做,得到了一些比较好的效果。以蚂蚁风控场景为例,HBase的线上young GC时间从120ms减小到15ms,结合阿里巴巴JDK团队提供的利器——AliGC,进一步在实验室压测环境作到了5ms。本文主要介绍咱们过去在这方面的一些工做和技术思想。node
背景算法
JVM的GC机制对开发者屏蔽了内存管理的细节,提升了开发效率。提及GC,不少人的第一反应多是JVM长时间停顿或者FGC致使进程卡死不可服务的状况。但就HBase这样的大数据存储服务而言,JVM带来的GC挑战至关复杂和艰难。缘由有三:缓存
一、内存规模巨大。线上HBase进程多数为96G大堆,今年新机型已经上线部分160G以上的堆配置服务器
二、对象状态复杂。HBase服务器内部会维护大量的读写cache,达到数十GB的规模。HBase以表格的形式提供有序的服务数据,数据以必定的结构组织起来,这些数据结构产生了过亿级别的对象和引用数据结构
三、young GC频率高。访问压力越大,young区的内存消耗越快,部分繁忙的集群能够达到每秒1~2次youngGC, 大的young区能够减小GC频率,可是会带来更大的young GC停顿,损害业务的实时性需求。并发
思路高并发
1.HBase做为一个存储系统,使用了大量的内存做为写buffer和读cache,好比96G的大堆(4G young + 92G old)下,写buffer+读cache会占用70%以上的内存(约70G),自己堆内的内存水位会控制在85%,而剩余的占用内存就只有在10G之内了。因此,若是咱们能在应用层面自管理好这70G+的内存,那么对于JVM而言,百G大堆的GC压力就会等价于10G小堆的GC压力,而且将来面对更大的堆也不会恶化膨胀。 在这个解决思路下,咱们线上的young GC时间得到了从120ms到15ms的优化效果。性能
2.在一个高吞吐的数据密集型服务系统中,大量的临时对象被频繁建立与回收,如何可以针对性管理这些临时对象的分配与回收,AliJDK团队研发了一种新的基于租户的GC算法—AliGC。集团HBase基于这个新的AliGC算法进行改造,咱们在实验室中压测的young GC时间从15ms减小到5ms,这是一个不曾指望的极致效果。大数据
下面将逐一介绍Ali-HBase版本GC优化所使用的关键技术。优化
消灭一亿个对象:更快更省的CCSMap
目前HBase使用的存储模型是LSMTree模型,写入的数据会在内存中暂存到必定规模后再dump到磁盘上造成文件。
下面咱们将其简称为写缓存。写缓存是可查询的,这就要求数据在内存中有序。为了提升并发读写效率,并达成数据有序且支持seek&scan的基本要求,SkipList是使用得比较普遍的数据结构。
咱们以JDK自带的ConcurrentSkipListMap为例子进行分析,它有下面三个问题:
1.内部对象繁多。每存储一个元素,平均须要4个对象(index+node+key+value,平均层高为1)
2.新插入的对象在young区,老对象在old区。当不断插入元素时,内部的引用关系会频繁发生变化,不管是ParNew算法的CardTable标记,仍是G1算法的RSet标记,都有可能触发old区扫描。
3.业务写入的KeyValue元素并非规整长度的,当它晋升到old区时,可能产生大量的内存碎片。
问题1使得young区GC的对象扫描成本很高,young GC时晋升对象更多。问题2使得young GC时须要扫描的old区域会扩大。问题3使得内存碎片化致使的FGC几率升高。当写入的元素较小时,问题会变得更加严重。咱们曾对线上的RegionServer进程进行统计,活跃Objects有1亿2千万之多!
分析完当前young GC的最大敌人后,一个大胆的想法就产生了,既然写缓存的分配,访问,销毁,回收都是由咱们来管理的,若是让JVM“看不到”写缓存,咱们本身来管理写缓存的生命周期,GC问题天然也就迎刃而解了。
提及让JVM“看不到”,可能不少人想到的是off-heap的解决方案,可是这对写缓存来讲没那么简单,由于即便把KeyValue放到offheap,也没法避免问题1和问题2。而1和2也是young GC的最大困扰。
问题如今被转化成了:如何不使用JVM对象来构建一个有序的支持并发访问的Map。
固然咱们也不能接受性能损失,由于写入Map的速度和HBase的写吞吐息息相关。
需求再次强化:如何不使用对象来构建一个有序的支持并发访问的Map,且不能有性能损失。