从0到1:饿了么大数据平台Hadoop集群规模突破1000+之炼金术
作者:互联网
随着接入的需求方越来越多样化,对大数据的数据使用、数据存储与计算的需求也越来越多样化,同时业务飞速发展,集群的规模也急速扩大。如何在这样的场景下通过大数据平台,稳定支撑住业务的发展是一个不小的挑战。本文分享主要平台工具链,技术、选型及架构设计上的一点经验。
大数据平台现状
饿了么的大数据平台团队成立于2015年5月份左右,在16年4月份,Hadoop集群规模还只在100+节点数,而在一年时间里集群规模快速增长到1000+的水平,这还是在引入数据生命周期进行管控的情况下的规模增速;同样,流计算集群的规模虽然相对较小,但也经历了10倍的增长,一些topic的吞吐量已超过百万每秒。
当前平台部分的逻辑架构如图1,并持续演进。
图1 饿了么大数据平台的逻辑架构图
当初面临的问题
饿了么已经成立9年时间,相对而言数据平台团队非常年轻,在加入团队之初面临了如下挑战:
人少活多 积累不足
内在质量 “差不多就行”
故障处理 “千人千面”
因此,主要以效率、质量和持续扩展为核心来建设数据平台。
技术选型
如图2所示,大数据的技术栈非常多样化,对于团队很多初入大数据领域的成员来说很容易在尝新过程中消耗团队的生产力,因此在加入团队初期,首先就要确定在当时条件下的技术选型。
图2 多样化的大数据技术栈
选型原则
在技术选型方面坚持的原则是“3T”:要解决什么样的问题和场景(Trouble),有哪些技术可供选择(Technology),以及团队技术栈与目标采用技术的匹配程度或者说掌控能力(Team)。
下面举几例子来看:
即席查询引擎选型
在以Hive on Hadoop为中心的离线数据仓库,最开始分析师以及数据工程师也都是使用Hive来做数据分析和探索,但是Hive本质上是基于MapReduce架构的,并不是很适合这个场景。当时所选择的目标集中在Presto和SparkSQL上,社区活跃度Spark是最高的,并且从SQL语法兼容性来看SparkSQL也是最合适的,用户的使用成本比较低,但是在测试的时候发现失败率高达50%,在兼容性和稳定性方面如果无法对Spark代码做一定定制化开发的话达不到我们的要求,相对而言Presto虽然语法兼容性不如SparkSQL,但是比较稳定并且在运行效率上也高于SparkSQL,考虑到当时团队的Spark力量积累不足,同时团队成员也有曾使用和管理过Presto的经验,因此优先考虑Presto作为Ad-hoc的查询引擎。
海量Key-Value存储选型
对于比如用户画像、历史订单等场景通常是需要一款能够支持海量K-V存取的存储引擎,大多基于Hadoop的团队使用HBase是比较自然的选择,同时也是我个人过去使用比较多的存储之一,但是发现其他成员对于HBase的掌控能力非常薄弱,同时周边的配套设施也没有建设起来,是否坚持使用HBase需要尽快想清楚。从HBase的架构上看(如图4),整体建构组件多(NameNode、DataNode、JournalNode、Zookeeper、RegionServer、HMaster),运维成本高;同时对非Java客户端还需要再引入thrift server,为了HA和负载均衡还需要一层LB,进一步加大了系统的复杂度。这时我们把目光转向了Cassandra:结构简单便于聚焦,多语言支持CQL,客户端功能丰富,访问控制也更成熟。另外,对于Cassandra一致性的顾虑在团队的场景应用中是比较容易规避的,大多数都是定期推送频繁读取,推送具有幂等性且场景对于一致性的要求不严格,同时反而可以利用Cassandra的多点提供读能力增加读取的吞吐量(对应的,HBase只有一个数据节点提供读请求)。最后决定把已有基于HBase的数据接口迁移到Cassandra来,降低了开发和维护的成本。
图4 HBase架构图
通过以上的例子总结下选型心得:
关于feature的选择:优点决定是否要谈恋爱,不过就算优点再好如果缺点接受不了,很难在一起幸福地过一辈子;
关于决策:喜欢是放肆,爱是克制。
架构设计
技术选型确定了,接下来需要解决在业务急速增长情况下的架构设计问题。理想的架构是系统上线后尽量减少人的参与,或通过简单的流程即可应对外部变化,追求可持续扩展的架构设计,这里通过一个具体案例来表达我们在设计时的关注点。
如图5,流入三个源数据流:用户行为、主站订单、以及开放平台订单的订单渠道,进行各种实时指标的计算,其中分渠道订单相关指标的计算和多维度组合下的UV计算场景是比较典型的流计算问题。
图5 流入三个源数据流的UV计算渠道订单
异步
分渠道订单指标计算需要将主站订单流和开发平台订单流进行Join计算,因为是多数据流的合并计算,所以在设计该架构时基于的假设是:不同源数据流之间的到达时间无法协同,我们将问题转化为”在可调整的时间窗口内通过匹配触发Join计算”。具体落地则是通过Redis缓存住还没有匹配的订单数据,引入时间窗口是为了控制住缓存的大小,而时间窗口的控制有两处:在拿到数据时会检查是否在时间窗口内;另外在未匹配的情况下写入Redis时会把时间窗口通过TTL的方式一并维护,避免多余的维护任务。
可扩展性
UV计算首先要解决的就是去重问题,比如判断某个deviceID是否是当天的平台新访客,一种做法是通过Redis的集合来判断,具体数据结构如下:
key : YYYYMMDD_uv
value : deviceID的集合
这样的设计会带来热点问题,所有该维度的deviceID请求都会打到一个节点上产生热点,当流量增加时无法通过直接扩容来解决问题,那么自然就想到如下的数据结构:
key : _YYYYMMDD
value : 占位符
通过如此转化可以很好地把请求打散,有具更好的扩展性。
回到多维度UV计算的场景下,通常涉及到的组合维度可以达到2的N次方,如果采用上述结构无论是读写的吞吐还是空间的消耗都是巨大的,扩展成本非常高,我们选择牺牲一定精度来达到低成本的扩展性。UV计算本质是基数估计问题,在该领域非常出名的数据结构就是HyperLogLog(以下简称HLL),虽然Redis本身支持HLL但是无法避免热点问题,我们选择在流计算过程中本地计算HLL,因为HLL支持merge操作同时幂等可回放,大量的计算都在计算任务节点本地完成,无论是shuffle还是落地存储的处理毫无压力,通过压测,在不扩容的情况下可以支撑20倍的压力。
稳定性
对于稳定性主要通过事前、事中和事后三个方面来看,即执行计划、故障处理和事后复盘。
执行计划
首先线上变更为了控制风险,有两点是必须遵守的:一定要有可行的回滚方案,一定要灰度。
其次,对于具体的尚未自动化支持的变更流程或SOP需要考虑异常分支,大多数看到的SOP文档只是考虑正常流程一步步执行下来,可是经常遇到的问题反而是某个流程走不通或者出问题了。
最后,就是变更时间估算很重要,对变更的节奏把握的越清楚风险越从容。
故障处理
对于故障处理我们比较关注的一个指标就是MTTR(Mean Time To Recovery,即平均恢复时间)。
故障恢复时间=告警响应时间+介入处理时间
从上面的公式可以看出MTTR主要是由响应时间和处理时间构成。
监控 ≠ 告警
对于稳定性来说监控是底线,但是”监”而无”控”的现象非常普遍,带来的结果是收到一个告警不知道如何处理,或者忽略掉,或者“千人千面”处理,问题不同程度地被隐藏或放大。
监控 = metrics+trigger+action
那么如何“控”呢?
监控的”控”
我们坚持如下次序:低成本的自愈优先于流程或SOP,如果SOP没有覆盖的场景就需要一个原则来指导方向,比如故障发生后是优先保哪个方面,一致性还是可用性。逐步迭代将故障处理的“千人千面”收敛到从容有序。
复盘原则
故障复盘对于系统稳定性的提高是个非常非常有价值的闭环反馈,在这方面我们实践着Facebook的DREP原则,同时基于实践经验引入了W9(Workaround),强调可持续的稳定性。
图6 Facebook的DREP原则
工具链
上文提到的技术选型及架构设计和稳定性保障通常依赖于人,我们更希望将人的经验构建在工具中,减少对人的依赖,提升组织的可扩展性。图7为工具链的架构图。
图7 工具链架构图
尽量扩大工具在整个数据工作生命周期的覆盖度,为数据工作人员赋能,主要包括:
元数据管理:指标管理,数据质量监控,血缘关系追溯等;
权限管理及数据安全:数据底层的安全,基础设施权限体系打通,以及数据使用安全;
数据开发管理:数据表管理,数据探查,离线与实时数据开发和任务运营;
数据应用:数据接口开发(SQL即接口),数据报表开发(SQL即报表)和管理;
自动化运营:整个基础设施的管理,包括CMDB、工作流引擎、容量规划、性能分析与告警管控等。
本文着重分享数据开发管理和数据报表开发。
图8 数据表管理系统功能示意图
数据表管理
生产数据表是所有数据开发工作的源头,因此我们把生产数据表的创建及维护工作统一收到数据表管理系统(以下称dtmeta)中,除了建表的基础功能外,主要关注如下信息:
静态数据:表所属主题,字段是维度还是度量,是否敏感或加密字段,表的生命周期和备份周期以及表的物理结构信息等;
动态数据:主要包含表的读写热度情况,以及表的容量变化情况,便于针对性策略优化和问题分析。
有了这些信息,减少了大量后续维护的工作,降低交互成本。
数据开发及任务管理系统
数据开发
模板化:数据开发工作者可以直接在系统中开发ETL任务,支持动态变量,同时可配置触发方式、期望完成时间等属性,作为特征提供给调度系统;
hook:在任务启动前和执行结束后可以触发的action,比如数据源的延迟检测,数据抽取后的数据校验或者推送后临时数据状态的清理(触发)等;
依赖识别:对于基于依赖触发的任务来说,依赖的自动化识别非常必要,人工配置依赖会遇到循环依赖以及依赖遗漏,从而影响任务的SLA甚至数据质量;
多数据存储推送 :Hive通过外部表的方式支持向ES、Redis、Cassandra、MongoDB等数据存储的推送以及抽取,简化数据开发过程中的数据交换工作。
图9 Titan调度平台编辑任务截图示意
图10 Hive的多数据存储推送
任务执行与管理
对于任务执行和任务的自助化运营管理我们主要关注这几点:
压力感知:会感任务运行的目标系统比如Yarn的压力,达到反压的效果而不是持续将任务直接提交给目标系统,往往会触发下游系统的Bug导致雪崩;
多引擎执行:对于HQL的任务,可以透明切换到Hive和Spark执行,目前小时频率的核心任务已经都稳定跑在Spark引擎上;
图11 错误日志的常见处理策略
链路分析
1. DAG出度分析,评估任务重要程度,同时也是提供给调度系统的重要特征;
2. 运行趋势分析,包括启动时间,运行时长,处理数据量的趋势变化;
3. 通过埋点将用户级别的任务和下游系统(Hadoop等)的任务全链路打通,可以追溯到任何一个层面执行状况;同时,会给任务打标签,比如倾斜、参数不合理等提供给用户进行快速的自助分析和管理;
4. 对错误日志进行归类处理,去掉噪音,并附上常见的处理策略,进一步提升任务自助化管理。
告警:可以设置灵活的告警策略和触达渠道,主要是辅助任务负责人或者值班人员。
报表开发平台
报表开发是数据应用非常常见的一个场景,在大数据部门成立初期有大量的报表开发工作需要消耗很多人力,虽然有很多成熟的商业产品,但是大多专注于交互可视化,对于已有系统和基础设施的接入成本很高,因此我们快速开发了报表开发平台(EMA)。
可以将模板化的SQL快速转成报表嵌入到各个系统中,并且和内部系统打通,血缘建立,支持包括MySQL/Preso/Kylin/Hive/Spark等各种常见的数据源或执行引擎,同时可配置报表查询缓存使得大计算量小结果集的场景得到很好满足。EMA上线至今,有接近八成的报表都是出自该系统。
图12 报表开发平台应用
实时开发平台
在线算法的实时特征计算包括POI感知、上下文场景感知,都是很典型的实时计算场景。实时开发管理平台主要包括数据源的端到端接入,封装框架的业务无关细节,提供可配置策略,另外利用flux将任务配置和拓扑管理抽象出来。任务的发布控制以及上线后自动监控联动,让开发人员更多关注业务逻辑和架构设计,减少管理层面的投入。
图13 实时开发平台的架构设计
平台的一些思考
沟通和协调是最大的成本,Do not take things personally
1. 面向用户:尽量推动助化,和产品的自解释;
2. 反复强化用户预期;
3. 面向系统:推动系统的自动化和一键化,最后才是SOP。
What gets measured gets fixed
设计方面
1. Less is more
2. Think about future, design with flexibility,but only implement for production
技术或方案选型
最合适的,而不是最先进的
清楚假设的边界
技术要面向业务效率,产品不足服务凑
以上是我们截止到17年H1的一个回顾,饿了么大数据平台还在持续快速演进中,期待有更多的干货在接下来能够和各位技术同仁共同交流探讨。
欢迎学Java和大数据的朋友们加入java架构交流:736925717
加群链接:https://jq.qq.com/?_wv=1027&k=5XXrrMk
群内提供免费的架构资料还有:Java工程化、高性能及分布式、高性能、深入浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的免费直播讲解 可以进来一起学习交流哦
标签:炼金术,平台,Hadoop,任务,开发,计算,选型,数据,1000 来源: https://blog.51cto.com/14224832/2362560