|0x00 从实时数仓的历史谈起
实时数仓的历史,有三个显著的分水岭。
第一个分水岭是从无到有,随着以Storm为代表的实时计算框架出现,大数据从此摆脱了MapReduce单一的计算方式,有了当天算当天数据的能力。
第二个分水岭是是从有到全,以Lambad和Kappa为代表的架构,能够将实时与离线架构结合在一起,一套产品可以实现多种数据更新策略。
第三个分水岭是从全到简,以Flink为代表的支持窗口计算的流式框架出现,使离线和实时的逻辑能够统一起来,一套代码实现两种更新策略,避免了因为开发方式不统一导致的数据不一致问题。
未来会有第四个分水岭,就是从架构走向工具,以Hologres为代表的HSAP引擎,用服务分析一体化的设计理念,极有可能彻底统一掉分析型数据库和业务数据库,再配合Flink,真正实现数仓的彻底实时化。未来不再存在离线和实时的区分,Flink + Hologres,用一套结构、一套产品,就完事了。
过去,面对实时,数仓的逻辑是:性能不够,架构来补;
现在,面对实时,数仓的逻辑是:既要、还要、全都要。
过去,我们使用OLAP引擎,是为了更快的查询数据;
现在,我们使用OLAP引擎,是为了当线上业务库,替代Mysql用。
当我们觉得摩尔定律要失效时,它却仍然在发展。当我们觉得数仓架构定型时,它却依然在高速演化。只有了解历史,才能看清未来,我想重新了解一下Lambda/Kappa/Flink的历史,熟悉架构的演进,以此来探讨实时数仓建设的一些问题。
我们先从Lambda的历史讲起。
|0x01 Lambda
对低成本规模化的数据服务,以及尽可能低延迟的数据应用,是推动Lambda架构诞生的原动力。
早期的Storm框架,能够帮助解决低延迟的问题,但这个框架并不完美,其中一个痛点,便是Storm无法支持基于时间窗口的逻辑处理,这导致实时流无法计算跨周期的逻辑,用户不得不寻找一些额外的方法来实现。为了解决痛点,Storm的作者Nathan Marz,提出了一种混合计算的方式,通过批量MapReduce提供离线结果,同时通过Storm提供最新的数据,这种离线与实时混合的框架,就是如今广为流传的“Lambda架构”。
亚马逊上有一本《Big Data: Principles and best practices of scalable realtime data systems》,就是Nathan Marz用来详细阐述Lambda架构的,而最重要的图,就是下面这张:
Lambda架构通过两条分支来整合实时计算和离线计算, 一条叫Speed Layer,用于提供实时数据, 另一条叫作Batch Layer & Serving Layer,用于处理离线的数据。
为什么这么设计呢?在深入理解Lambda之前,我们首先了解一下数据系统的本质,主要有两个:一个是数据,另一个是查询。
数据的本质,主要体现在三个方面:
when:数据的时间特性,对于分布式系统而言,这个特别重要,因为时间决定了数据发生的先后顺序,也决定了数据计算的先后顺序;what:数据的内容特性,数据本身是不可变的,分布式系统对数据的操作,可以简化为两点:读取和新增,也就是Create和Read,而Update和Delete可以用Create来替代;where:数据的存储特性,基于分布式系统数据不可变的特性,存储数据只需要进行添加即可,系统的逻辑设计会更加简单,容错性也可以大幅度提升。查询的本质,特指一个公式:Query = Function(All Data)。
表面的意思是,不论数据是存在Mysql这种RDBMS的,还是MPP类型的,或者是OLAP、OLTP,甚至是文件系统,都能够支持查询。
更深入一点,意味着数据的查询,要支持Monoid特性,例如整数的加法就要支持:(a+b)+c=a+(b+c)。
概念本身比较抽象,这里只需要了解,如果函数满足Monoid特性,那么计算时就可以支持分散到多台计算机,进行并行计算。
了解了数据系统的本质,我们再看实时架构,我们就发现,如何在平衡性能和资源消耗的基础上,满足Query = Function(All Data)的特性,就是Lambda架构设计的精髓。
离线数据能够支持庞大数据量的计算,耗时长,但处理的数据量大,适合历史数据的处理。并且,因为我们不能避免系统或者人为发生的错误,那么离线方案对于结果的兜底,要远优于实时方案,任何问题都可以通过逻辑的修正,来得到最终正确的结果。所以查询应该是:
batch view=function(all data)
而实时数据要支持实时的部分,为了实现低延迟的特性,势必要减少计算的数据量,那么只针对增量的做处理,作为对离线处理的补充,就是一种最优的方案。这里查询就变成了:
realtime view=function(realtime view,new data)
而最终的结果,因为要支持历史+实时数据的查询,所以需要合并前面的两个结果集。如果我们的查询函数满足Monoid性质的话,只需要简单合并两个流的数据,就能得到最终的结果。所以:
query=function(batch view, realtime view)
总结下来,Lambda架构就是如下的三个等式:
batch view = function(all data)
realtime view = function(realtime view, new data)
query = function(batch view, realtime view)
这么设计的优点,Nathan Marz也进行了总结,最重要的有三条,即:
高容错:对于分布式系统而言,机器宕机是很普遍的情况,因此架构的设计需要是非常健壮的,即便是机器跪了,任务也要能正常执行。除此之外,由于人的操作也很容易出现问题,因此系统的复杂性要控制在一定的程度内。复杂度越高,出错概率越大。低延迟:实时计算对于延迟的要求很高,对应的系统读操作和写操作应该是低延迟的,对系统数据的查询响应也应该是低延迟的。可扩展:系统不仅要能够适应多种业务形态,比如电商、金融等场景,当数据量或负载突然增加时,系统也应该通过增加更多的机器来维持架构性能。因此,可扩展性,应该是scale out(增加机器的个数),而不是scale up(增强机器的性能)。最后,Lambda的架构,就设计成了我们经常看到的样子:
尽管结果看起来是“正确的废话”,但思考的过程,却是非常的透彻和深入。这本书很不错,但了解的人不多,感兴趣的可以了解一下。
|0x02 Kappa
Lambad框架尽管考虑的很深入了,但仍然存在两个问题:
第一个是数据复杂度高,由于有多套数据源,因此口径对齐就成为了一个大问题;同时,产品在建设时,交互上要考虑实时和离线两套逻辑,面对逻辑多一点的业务,计算也会变得非常复杂;第二个是搭建成本高:不仅框架需要维护多套,开发人员需要熟悉多种框架,而且相互之间运维成本很高。离线和实时维护两套逻辑,太容易导致数据结果不一致,这个点非常痛!LinkedIn的Jay Kreps对此很不满意,于是提出了“Kappa架构”,作为Lambda方案的简化版,删除了批处理系统的逻辑,认为数据只需要流式处理就可以。
这个方案的设计其实有一些暴力,让我们看下主体思路是怎样的:
以Kafka作为数据的存储系统;当需要计算增量数据时,只需要订阅相应的broker,读取增量即可;当需要计算全量数据时,启动一个流式计算的实例,从头进行计算;当新的实例完成后,停止旧的实例,并删除旧的结果。架构上应该是这样样子:
逻辑上是这个样子:
有一句话很适合Kappa架构,即“如无必要,勿增实体”,也就是只有在有必要的时候才会对历史数据进行重复计算。由于实时计算跟离线计算是同一套代码,因此规避了两套逻辑带来的结果不一致问题。但相应的,Kappa的性能其实就成为了一个问题,需要对实例的数量进行控制。
感兴趣的,在这里进行扩展阅读:
http://milinda.pathirage.org/kappa-architecture.com/
|0x03 Flink
我们经常听说 "天下武功,唯快不破",大概意思是说 "任何一种武功的招数都是有拆招的,唯有速度快,快到对手根本来不及反应,你就将对手KO了,对手没有机会拆招,所以唯快不破"。Apache Flink是Native Streaming(纯流式)计算引擎,在实时计算场景最关心的就是"快",也就是 "低延时"。
Flink最近实在太火了,明面上,它有这些优点:
丰富的流式处理用例:事件驱动型应用程序;支持流式/批量处理;支持数据通道及ETL。正确性有保障:严格执行一次机制;基于事件时间的处理;复杂情况下的延迟数据处理。分层API机制:流式计算SQL与批量处理数据共存;数据流API与数据集API共存;基于时间和状态的过程控制。但实际上,Flink通过窗口和时间两个大的特性,有效解决了数据的乱序、周期计算问题,再配合SQL层统一逻辑,解决了一套代码,同时支持实时和离线两种模式。过去通过架构来解决性能不够的问题,一下子变得不再重要了。
也许有很多人有质疑,Spark也支持流批的模式,它俩的区别在哪?Spark和Flink虽然都希望能够将流处理和批处理统一起来处理,但两者的实现方式却各不相同。
Spark是以批处理的技术为根本,并尝试在批处理之上支持流计算;Flink则认为流计算技术是最基本的,在流计算的基础之上支持批处理。
因为这种设计理念的差异,二者存在一些比较显著的差异。例如在低延迟场景中,由于Spark是基于批处理的方式进行流式计算,因而在运行的过程中存在一些额外的开销,如果遇到对延迟的要求非常苛刻的场景,例如百毫秒甚至十毫秒级别,Flink就存在显著的优势。
讲Flink的文章很多,这里就不再赘述了,希望系统学习的,还是要去官方网站看看:http://flink.iteblog.com/。
这里也推荐一个大神的博客:https://enjoyment.cool/catalog/
|0xFF 实时数仓的设计
讲完了实时架构,我们再看实时数仓。
我们已经对离线数仓怎么做很清楚了,但实时数仓要怎么做,则没有很明确的方法论。有一些观点是,实时不需要数仓,也有一些观点是,实时像离线一样做数仓就行了。这其实都是不对的。
新中国在造核潜艇时,原本以为核潜艇 = 潜艇 + 核动力,不就是潜艇换一下动力源嘛,有什么不同的,但实际做的时候才发现,核潜艇与潜艇,完全是两个物种。今天很多人在看轰-6K的时候,样子与轰6差不多,那它们应该是同一种飞机,但6K和6之间,除了壳子一样,里面的东西,已经完全没什么相似的了。
同样,实时数仓与离线数仓,看起来是类似的,但实质却是不同的。
例如,这里提一个问题,实时数仓需要维度建模吗?也可以说是的,也可以说不需要,这主要取决于业务的复杂度。其实离线数仓,在业务不复杂的情况下,也不需要,直接加工目标数据库就行。如果离线都没必要,那么实时更没必要。但如果业务搞的非常复杂,那么不仅离线需要,实时也就需要。
但,离线与实时,数仓的挑战和思路,还是有一些不同的。
实时数仓的挑战,主要体现在四个方面:
时效性挑战:实时数仓如果延迟大了,那么跟分钟级计算就没有本质区别,因此如何尽可能的快,就是实时数仓最大的挑战;准确性挑战:离线数据有完整的质量保证体系,但这些在实时数仓还都是比较新的挑战,过去我们通过流批一体解决了多数据源带来的结果不一致性,但如果保证开发结果的准确,挑战依旧很大;稳定性挑战:实时数仓与离线不同,数据计算过程中出现了错误,修补的成本非常高,甚至可能导致永久性的数据丢失;灵活性挑战:离线数仓最大的特点,就是可以应对海量需求的开发挑战,而实时数仓一旦运行任务太多,不论对开发还是运维,挑战都是很大的,尤其是面对需求频繁变更的场景。从数仓的规范上看,各层的变化如下:
ODS:由于流批一体统一了数据源,而且ODS原本就不对原始数据做处理,因此可以无需再建表,直接用数据源即可;DWD:与离线类似,实时业务也需要构建事实明细表,但区别在于,离线方案中,我们为了提高明细表的使用便捷程度,往往会把一些常用的维度退化到事实表,但实时方案出于时效性的考虑,倾向于不退化或者只退化不会变的维度;DWS:这一层根据业务情况的不同,在实时数仓的建设策略上,差异比较大;通常情况下,离线建设DWS,是针对比较成熟的业务,将维度逐级上卷;这样做能够将逻辑进行收口,提高下游使用的复用率,但缺点就是做的比较厚重;如果实时业务不是那么复杂,那么就不建议将DWS建的很重;究其原因,汇总层的目的在于预计算、提升效率和保证指标的一致性,实时链路太长,容易造成高的延迟和比较大的资源消耗;DIM:维表在实时计算中非常重要,也是重点维护的部分,维表需要实时更新,且下游基于最新的维表进行计算;ADS:功能和用途不变。除了维度建模,现在依然面临几个问题。
第一个是实时数仓是否能完全替代离线数仓。
答案是不能完全替代。尤其是对结果准确性要求非常高的场景,离线可以通过数据回刷等措施来对结果进行兜底,但实时一旦数据源出错,就很难再进行弥补了。
第二个是公共层是否有必要建设。
答案是有必要。如果面对流量暴涨的业务场景,如果实时没有公共层缩减计算数据量的话,一些数据倾斜场景很有可能直接干爆了整个实时体系。
第三个是资源的错峰使用。
实时数仓的方案通常比较贵,为了解决成本压力,通常会与离线方案进行混布。凌晨实时压力小、离线压力大,而白天实时压力大、离线压力小,这时就可以充分根据集群资源运行情况,将水位线抹平。
后话:
其实本文涉及了两个岗位的内容:数据开发和数据仓库,从分工来看,数据开发更偏向底层,侧重于工具的优化;数据仓库更偏向业务,侧重业务的最优实现。现代数据体系,单一岗位很难把所有的事情都做了(开发、数仓、分析、算法),但又需要你懂所有岗位在做什么。虽然我们都有共同的目标:数据科学家,但复合型人才,尤其是一专多能的人才,是迈向数据科学家过程中,检验阶段性结果的一步。
本文只是简单描述了一下实时数仓用到的一些原理性知识,以及部分的实践知识,要想出真知,还是需要自己去实践。大公司之所以是普通人向往的目标,正是因为大公司提供了最好的实践机会,并不是做的有多好,只是练的足够多。
举报/反馈

晓阳的数据小站

5获赞 17粉丝
通过数据,让思考更多一点。
关注
0
0
收藏
分享