从未降级的搜索技术 – HBase集群升级与优化

战争从来都是拼后勤拼平台支撑的,天猫双十一这一天对于我们搜索事业部来说,就是一场高强度的数字化战争。为了这一天,各兄弟业务线的战友们已经摩拳擦掌,纷纷亮出各种新式武器,而我们原有的离线系统平台却渐渐显出疲态,慢慢被来自各业务线的不断提升的压力需求搞得捉襟见肘了。个性化搜索实时数据处理平台(Pora)在双十一将正式亮相,当时我们预计会有数以十亿计的新增HBase读写请求,如果不进行升级优化,原有的离线集群预计将无法承受这一前所未有的压力;天猫业务线的增量在双十一更是重中之重,届时预计会有数倍甚至十多倍的增长,不断流,不延迟对于原有的离线集群来说也是巨大的考验;主搜、国际站等业务线也都对底层平台提出了越来越高的要求,凌晨全量的时间极其有限,不能出现任何闪失。如何有效应对以上这些复杂且艰巨的挑战就成为我们离线系统团队最紧迫最核心的课题。HBase-0.98版本就是我们就对挑战的新型“核动力航母”,在这个平台上,我们有效地支撑各相关业务的发展,最终有惊无险地度过了双十一,顺利通过了考验。

发展历程

Hadoop/HBase技术被引入阿里的搜索体系是在2010年夏天,初始版本号是hadoop-0.20.2和hbase-0.20.5。当时的导购搜索项目急需寻找一种高可靠高性能的分布式存储系统,用于存储导购相关的网页、价格和图片等信息,同时需要进行大量的随机读写和批量扫描、数据挖掘等工作。2011年,我们的HBase升级到0.90版本;2012年初,HBase再次升级至0.92版本;再后来,2013年初,主搜和Etao的离线集群都进入了Hadoop-2.0和HBase-0.94时代。从那以后,我们有一年半的时间没有对集群进行过大的版本升级,直到2014年8月初,etao离线集群率先升级至HBase-0.98版本,紧接着,主搜cm8集群也在10月初步入HBase-0.98时代。计算模型方面,我们基于hadoop全新的YARN框架开发出了iStream实时流计算模型,从而形成了全量、增量加实时的分布式计算+存储一体化的解决方案。在主搜cm8集群中,全量、增量及实时计算的资源(CPU和内存)占比分别是50%,13%和37%;而Etao离线集群三项的占比分别是39%,3%和58%。集群的机器数量也从2010年最初的单一集群40台节点,一步一步进化到现在,形成了一个主搜cm8近700台,etao近500台,整体上近1200台节点的搜索离线集群。

技术调研

HBase-0.98版本的前期技术调研始于2014年初,当时的调研结果是非常令人满意的,以下几个大的变化让我们充满期待:

  • 具有更好的读性能。在开发集群使用YCSB压测结果显示,相对于HBase-0.94版本,HBase-0.98的Get操作的latency下降60%,Scan的latency下降40%;
  • MTTR(Mean Time To Recovery)机制让RegionServer的Failover恢复更快,实测显示,写操作可以在秒级恢复,读操作可以在十秒级恢复;
  • StripeCompaction策略优化了Compaction的IO使用,对于在搜索业务中广泛使用的HQueue时间序列式的表来说,优化会很明显;
  • 全新的启发式LoadBalancer,比原有HBase-0.94默认按region个数进行balance的策略更加有效提升系统的稳定性及性能,减少出现Region热点的概率;
  • ProtocolBuffer通信协议,让以后的跨大版本Rolling升级成为可能,避免了不必要的停服务操作。

除了技术调研和基本的性能评测以外,我们还要将自己在HBase-0.94中增加的各种patch移植到新版本中去,例如:

  • HTable的autoFlush批量写操作优化,减少rpc次数,提高写吞吐能力;
  • ThriftServer支持HQueue读写,并自动清理未关闭的Scanner,节约内存;
  • 网页端Table级别的数据查询工具
  • Metrics优化,由于0.98改用了HadoopMetrics2版本,相应的配置及metrics指标与以前完全不同了,为了对比的需要,我们重新把一些指标补充进来(例如HFile的read和pread的latency等),并更新ganglia配置,以适应新的指标
  • RegionServer的Rolling升级工具,让日常小版本升级对应用更加平滑透明
  • Region Split/Merge策略和工具
  • 还有一些Client端接口的向前兼容等

另外,我们还发现了社区版本中的一些小bug,及时地提交patch并被接受。

初试牛刀

HBase-0.98版本的前景很令人期待,但升级的过程还是比较坎坷的。Etao离线集群作为主搜cm8集群的backup集群,首先来承担了HBase-0.98的升级演练及功能、性能验证工作。这个过程我们踩了不少的“坑”,例如:我们的集群中使用了Phoenix项目(集群资源统计系统HStats以及新版的HistoryServer都依赖于它),相关的一些表中就使用了Phoenix定制的Coprocessor,而这些Coprocessor在HBase升级时,会保留一些旧版本的java package类名,这些类却不在新版本phoenix的jar中,导致hbase启动后加载这些带有phoenix定制coprocessor的表时出现失败,集群无法启动。最终我们是通过改hbase源代码加逻辑绕过这些表才恢复的。接下来,由于升级前后那段时间,公司对生产网机器都增加采用了长域名访问机制(以”tbsite.net”结尾),而之前我们集群中各种相关配置都是短域名,造成了HBase集群运维过程中的一些不一致现象,后来我们是通过统一采用长域名来解决的。还有一点,为了给集群版本升级降低压力,我们在升级前24小时(具体时间是根据集群实际规模及数据量来确定的),对HBase集群所有的表进行一次major_compaction,最大限度地减少体积,从而间接减少HBase启、停时间;另外,升级前24小时也要通知业务方清理HDFS上面的所有无用目录及文件,为hdfs减轻启、停压力。

在etao离线集群升级HBase-0.98完成后,我们立即就开始进行各主要功能点的验证工作,为主搜cm8集群的升级铺平道路。

  • 最给力的指标莫过于读性能的提升,由于etao集群上面的各业务对于HBase的读(随机Get+顺序Scan)要求比较高,因此效果很明显,凌晨全量的scan密集型的job普遍快了10~20%,而白天增量时段,集群的get的latency也下降30%左右,且更加平稳。
  • 由于采用了新的异步写WAL的机制,集群中由Put写操作产生的IO压力相比以前也变得更加稳定。
  • PrefixTree与DIFF方式的ENCODING相比,对于随机读(GET)操作来说,降低了约5%左右的latency。后来由于社区曝出在scan时有漏数据的问题,因此紧急下掉,等社区后面修复稳定后再考虑上线。
  • StripeCompaction在试用时发现,在Size比较大的HQueue上效果基本符合预期,但对于Size比较小的HQueue来说,再次分“Stripe”会导致小文件过多,因此这个功能也安排在双十一之后逐步上线。
  • 启发式的LoadBalancer比较让人失望,它的算法理论上是追求动态均衡的,考虑到了很多的因素(包括region个数,move次数,read/write请求数,storeSize,locality等),但在实际运行中常常造成集群中大量region的迁移,影响到上层应用的访问,而且,在计算locality因素时,常常会给hdfs很大的访问压力,因此这项功能最终被我们放弃了,还是恢复到0.94时的按region个数进行balance的策略。
  • 这段时期,最让人头痛的问题还是稳定性。稳定性是分布式存储平台的基础,是最最最基本的要求,但升级0.98后我们发现,常常有RegionServer会莫名其妙地写HDFS卡住,无法响应外部请求,直接严重影响了上层的应用,连续导致我们出现了3次P4和1次P3事故,尝尝收到来自淘点点业务的投诉,弄得我们焦头烂额。为此,我们一方面专门开发了一套HBase集群可用性监控及自动处理系统,能在几分钟内,尽快发现并解决掉已经卡住无法对外响应的RS;另一方面,我们投入多名工程师一起攻坚,最终解决了HBase写HDFS卡住的问题。原因主要分为两方面,一是集群机器操作系统配置中的net.core.somaxconn为默认值,之前在云梯集群发现过类似问题,调大此值到4000,应用后然后重启DN,情况大大改善;另一方面是hadoop-2.4 HDFS Client重构后未对socket设置超时时间,默认永不超时,导致卡住,后来我们fix了这个bug,HBase集群稳定性因此得以恢复。

背水一战

在经历了一番波折解决了前面的问题后,我们本以为可以高枕无忧了,因此,今年10月初,就急切地把主搜cm8集群也升级到HBase-0.98版本。大大出乎我们的意料的是,升级后的主搜离线集群性能及稳定性居然还不如升级之前!双十一日益临近,没有回头路的我们开始了新一轮的调优和改进。

连续挂RS的问题

关于cm8集群的稳定性问题,其实最重要的就是一点:每天上午总会连续挂掉数十台RS。去查它们的HBase日志,基本上都是GC太忙,OutOfMemory挂掉的。但由于都是直接进程消失,没有其他痕迹,导致我们无从查起,只能根据发生的时间来判断应该与上午的某个job的读写有关。幸运地是,我们在逐台“验尸”时,发现一台机器保留了hprof文件,把当时RS的内存完整保留了下来。经过profiling分析发现,原来是在cm8集群hbase中,有一张odin_stat的统计用的表,它里面中存放了不合理的数据,一次scan操作时,扫出的某单条数据row的size超过1G,另外还有几条size达到数百M的row,导致RS无法短时间内消化掉,最终OOM了。在我们停掉了这张表对应的扫表job后,连续挂RS的情况再也没有出现。后续我们推动业务方改用OpenTSDB这种系统来存储时间序列日志。

LoadBlancer的“坑”

主搜cm8离线集群升级后,很多的job跑得比升级前还慢,而且网络打满、IO集中的问题非常明显。刚开始我们都以为是yarn的调度问题是主因,后来一查发现,很多大表的region在集群上分布很不均匀,有些RS上十几个,有些RS上根本就没有。后来查了一下源码才知道原来是0.98把一个bytable的balance参数默认值给改了,0.94时bytable默认是true,也就是按table做均匀,每个table的region在集群上要均匀分布,而到了0.98时,这个参数默认变成了false,而且这个参数并没有出现在配置文件中,只静静地藏在源代码里。我们更新了配置并重新balance了一下,各表region恢复均匀,大部分job的运行时间恢复升级前水平,网络打满及IO压力集中的问题也得到了缓解。之前之所以在etao集群没有发现这个问题,原因是etao集群中有一个占据了集群资源超过80%的大表pagebase,这张表由于历史原因并不是presharding的,导致在集群的分布就不是很均衡,出现网络打满和IO压力大的情况也相对较常见,是它把balance不均的问题给掩盖了;而主搜cm8集群的表基本上都是presharding的,一旦region分布不均就会非常明显。

Bulkload优化

主搜及b2b业务线中大量使用bulkload来导入数据,针对这些操作,我们进行了多个细节的优化:一方面,降低bulkload生成的HFile文件个数,让每个Family每次只增加一个HFile,这样就减少了bulkload之后立即触发的compaction的次数,降低了磁盘IO和网络压力;之后,增加了bulkload前的压缩和encoding,使HFile尽量小一些,同样是为了降低网络和IO压力;最后,我们在bulkload前生成HFile的阶段增加了逻辑,让它能按照Region的locality状态来写HFile,这样就能使bulkload的文件直接就放到region本地,使接下来的扫表job能够更高效地运行。

HTablePool的问题

为了应对Pora的高压写操作,我们让业务方把相关的istream进程配置上了autoFlush=false的参数,也就是批量异步写HTable,最多延迟1秒,用时间换吞吐率。运行一段时间后发现集群中很多机器出现线程数过高导致进程强制退出的情况。通过ganglia看到线程的增长的主要贡献者就是pora,jstack了一下相关进程发现,多出来的线程都来自于autoFlush时使用的异步写线程。在etao离线集群稳定使用了两年多的autoFlush功能为什么会出现线程泄漏的问题呢?原来是HTablePool搞的鬼。Pora中普遍使用hbase源码中的HTablePool来获取HTable对象,这个结构本身并不保证HTable结束后会close掉,有些情况下会直接丢弃不close,而我们的autoFlush的异步写线程只有在HTable的close阶段才会关闭的,因此才导致的线程泄漏。HBase-0.98本身其实也有解决方案,就是使用HConnection来替换HTablePool,那里的线程池是安全可靠的。Pora团队已经将此项优化列入日程,预计双十二之后上线。

表结构方面的优化

由于历史原因,主搜cm8集群的很多HTable的参数都是有问题的,这次优化也对其进行了调整。有一部分表的MAX_FILESIZE参数设置得过小,导致HTable在偶尔会意外分裂,从而影响上层应用访问时的均匀和稳定;DATA_BLOCK_ENCODING配置成DIFF能节约一些空间,从而间接减少网络和IO的消耗;COMPRESSION方面,SNAPPY相对于主搜常用的LZO来说,压缩率差不多,但会省cpu,速度还稍快一点点;对于数据可靠性要求并不是特别高的HTable来说,可以把写WAL的方式改为异步的,从而提升了写性能,降低磁盘IO压力。另外,很多b2b的流程中常常需要重建hbase表,以前是直接在代码配置中去完成表参数的构建,现在推荐他们使用类似hbase shell中的truncate_preserve的方式来重建,这样就使业务方应用与HTable具体结构参数彻底脱离依赖关系,既方便集群运维管理,也简化了应用的逻辑。

战绩概览

上述优化改进完成后,通过Ganglia各项监控指标对比,主搜离线集群整体开始超越升级前水平,稳定性也优于之前版本。
从集群角度看:

  • 相比升级前,集团整体CPU和Load下降超过10%,且更加平稳;
  • 网络和磁盘IO写压力平均下降了20%,磁盘读IO基本不变;
  • HBase的Get和Mutate访问请求的99th_percentile的latency下降了约20%,且毛刺变少;
  • 双十一全天HBase离线集群的平均读写QPS达到了150万/秒,峰值QPS近700万/秒。

从应用角度看:

  • 在双十一当天,我们的主搜HBase-0.98离线集群顶住了压力,凌晨两点启动的主搜全量生产流程顺利按时产出,并按照预期开始追增量,追完增量后实时增量运行平稳
  • Pora在凌晨的增量高峰时,QPS分别达到pora-sync接近30万/s,pora-auction超过35万/s,pora-user接近40万/s;增量处理延迟都在几秒钟之内;全天为主搜、天猫、店铺内3个引擎总共推送了超过16亿的增量更新。

“核动力航母”已经起航,技术发展永无止境,我们接下来还有更多的事情要去做……

你可能感兴趣的:(hadoop,hbase,性能优化,分布式技术)