<>|0x00 从流批一体诞生的必然性说起
通常来讲,数据仓库的建设,都是以离线作为主要的密报,下游的应用,不论是报表还是接口,所提供的数据也大多是T-1时效性。
但伴随着业务的变化,当离线做到没什么可以继续做的时候,实时就会被拿出来,作为新一个阶段的目标进行攻克。
在流批一体建设之前,这种实时诉求通常会开发成分钟级的任务,通过近实时的方案来解决业务的问题,但分钟级会带来诸如任务过多、资源挤占较大、无法支持复杂逻辑等问题。
因此专门支持实时计算的框架,比如早期的Storm,能够尝试从纯实时的角度解决业务问题,就被拿出来作为尝试。然而Storm的局限性也很大,因为那会的任务开发只能通过Java的方式来进行,与Hive所推崇的纯SQL方案相比,上手难度大了不少,同时两套代码的逻辑几乎没有可比性,这种方案也就一直没有什么声音。
尽管实时技术有各种缺陷,但作为一种能够很容易讲清楚价值的项目,同时又非常便于向上汇报的技术方案,实时技术还是被或多或少的做了起来。在大多数的公司里,实时和离线就会有不同的团队进行维护,或者是同一个团队,但分成不同的项目来执行。这个阶段,优先高效的把业务做起来,哪怕场景再简单,但能够证明实时有价值和前景,这个阶段的目标就算完成了。
以上的各种方案,难免会带来三个特别难以解决的问题:
(1)数据的口径上,实时和离线很容易不统一;
(2)数据模型的规范上,实时和离线也往往是分开建设;
(3)即便是同一种口径和同一种规范,实时和离线也要分成两套代码来维护。
这三个问题短时间内会被高速发展掩盖掉,但当业务对实时的诉求越来越多、压力越来越大的时候,口径和代码的不统一,就会越来越成为阻碍敏捷开发的障碍,需要有方案进行解决。
后来Flink出现了,带来了流批一体的全新方案,这个问题便出现了解决的曙光,这也比较接近我们对于实时计算的理想方案,因为其意义堪比Hive,也成为了各个大厂面试的标配问题。
然而,仅仅学会Flink是不够的,因为流批一体带来的并不仅仅是技术方案或者是框架的改变,同样带来了数据模型的改变,这就要求我们从数据模型上,而不是技术方案上,来制定我们的实时方案。
<>|0x01 实时的数据模型方案是怎样的
那么我们如何理解“实时数据模型”这件事情呢?
通常而言,我们关心的内容,包括如下几个方面:
(1)实时数据源与离线数据源存在差异,导致相同的字段,取值或者类型会存在不相等的情况;
(2)实时和离线由于底层执行机制的不同,通常需要维护两套代码,会带来诸如口径不统一、质量检测难的问题;
(3)产品逻辑变化较快时,离线模型修改相对容易,但实时模型需要考虑压测、削峰、重启等技术问题,维护成本非常高昂。
数据仓库之所以能够普及并被业务接受,正是因为其模型能够屏蔽掉底层差异的问题,并且有相对可靠的数据质量监控方法,并且变更成本非常低。而实时数仓如果想要替代掉离线数仓,以上的问题通常是需要一些模型设计甚至是平台工具的来解决,这些问题解决的重要性,并不比Flink弱。
我们先从比较可控的模型层面说起。
在离线的概念里,数仓模型设计成了DWD/DWS/ADS三个层级,原本的概念是DWD面向事实表的构建,DWS面向公共指标的统一,ADS负责灵活的口径变化问题。
在离线的概念里,DWD/DWS/ADS三个层级需要保留,但负责的目标会有一些变化,同时还需要增加存储统一层,也就是以TiDB/Holo为代表的数据库,来承担服务分析一体化的诉求。
让我们先看DWD层,DWD承担了屏蔽实时离线链路差异的问题,最重要的作用是保证表结构的统一及字段内容的对齐。DWD最重要的意义,是保证离线表和实时表,其表结构和字段概念是相同的。
为什么这么强调?试想一下,在离线场景下,我们可以在DWD上灵活的增加各种统计标签,或者是将维度退化到事实表,都是一些left
join或者是服务端直接打标可以解决的事情。但在实时场景下,这会变成多流join或者是缓存等更复杂的技术场景,导致这些信息并不能有效的记录到DWD,因此DWD的设计就要产生一些变化,有一些内容在实时场景下无法准确记录,这一类信息需要标识到对应的字段描述上,下游使用时才不会出错。
同时,实时和离线存储数据的介质,也必然有一些区别。例如离线可以存在HDFS上,实时则可能视情况保存在数据库、HDFS甚至是内存中,这时候对于字段格式、读取方式都会有差异,设计表时其约束条件也会更多。
因而,DWD更多承担了逻辑统一的职责,依旧以事实表为基础,但约束条款要比离线更多。
再看一下DWS层,离线上DWS是负责口径统一的重要一环,将通用的维度和口径计算方法抽象出现,以提供跨数据域的灵活使用。但在实时场景下,这一类的维护收益通常都比较低,不仅因为实时只看当天的数据,也是因为实时本身的维度难度就较大,多一层模型其收益会急速下降,因而大多数时候会忽略掉DWS的建设,ADS直接引用DWD进行统计。
然而,DWS毕竟存储的内容要比DWD少很多,因此如果计算资源瓶颈非常明显,或者是业务场景不需要分析实时明细数据的情况下,或者是DWD的下游引用过多时,DWS可以承担削峰的重任,通过减少数据量以应对大促等场景,还是有一定意义的。
接下来就是最重要的ADS层,在这一层上,逻辑统一、口径统一、大促削峰在前置模型上都得到了一定程度的解决,ADS则像离线一样承担了应对需求变化的重任。
但ADS所面临的情况和离线还是有所不同的,因为ADS的任务启动,不仅要启动一个离线的跑批任务,还要同时启动一个实时的流式任务,而ADS往往会同时统计离线+实时的结果,以应对同比、环比等场景。
这时候很多具体Case要具体分析了,因为特定场景的坑会非常多。例如最常见的“同比”,要对比今年和去年的结果变化,离线往往会统计分小时的结果,但实时会累计起始时刻到当期时刻的结果,因而当一个小时没有结束的时候,这个同比的波动变化会非常大,给人一种“数据是错误的”印象,新手很容易踩这个坑,从而被业务质疑。
因此,针对累计统计指标,从代码设计上就要考虑到这种情况,都根据时间字段统计起始到当前时刻的结果的,在代码逻辑上会要求一些统计技巧。
很多时候,因为业务指标变化太快,改实时代码是来不及的,这时候一部分的工作量甚至需要报表工具的数据集来解决,改动查询sql,要比改动任务来的快捷多了。但这部分的能力,其实是依赖于存储工具的,个人认为可以分到存储统一层来解决。
最后是存储统一层,因为一些特殊的场景,比如实时分析明细数据,或者是不确定时间周期的多天统计结果,如果依赖Flink
SQL来解决是有些不现实的,因而这部分的压力需要数据库来承担。
简单讲,就是将明细做轻度的汇总后,直接写到数据库,实时更新,下游自定义条件,并直接读库统计结果。这种场景既要求数据库有OLAP的计算能力,也要有OLTP的稳定特点,因而TiDB和Holo这一类HTAP的引擎就变得非常重要。
<>|0x02 实时模型对于开发工具有哪些要求
因为多了实时的部分,因此过去面向离线的开发工具,也需要有一些特定的改造,以适应实时的开发和运维诉求。
对于开发工具而言,其目标集中在四个场景上:元数据定义与获取、数据建模、开发与测试、运维与监控。
首先讲元数据定义与获取,Flink通常对接能够支持发布订阅的消息流框架,因而其元数据并不像表一样有明确的定义,我们在获取消息流的数据格式和定义时,就需要专门的系统来承载。同时,离线和实时不同的地方,在于离线仅需要统计任务完成后的一些执行情况,而实时则需要将日志量的趋势展示出来。
其次讲数据建模,因为建模的理论已经稳定了有些年头了,绝大多数场景下都是按照既定的方案来执行。过去离线当道时,规范执行的弱一些不是什么大问题,但流批一体当道的年代,规范是需要强约束的,这就对了开发工具提出了一定的要求,是否能够从平台层面上对规范进行内置,并以此来约束开发的同学,降低不规范模型对后期维护带来的压力。
这种建模能力的代表有两种,一种是规范表的命名,填写相应的分层+主题域+数据域+统计刷新方式,从源头上规范表的目标和作用;一种是规范指标的定义和使用,例如原子指标还是派生指标,统计周期多少,业务限定用语如何规范,统计粒度怎么填写。
在实际开发中,通过工具的限制,如果规范可以做的好,代码是可以自动生成出来的。当然,以上的功能,都属于通过牺牲开发效率,来提升数据质量的范畴,使用时需要根据团队的情况来限定。
再次是开发和测试,这是平台提供的最重要的能力。在开发层面,就是代码的预编译能力+发布功能。预编译不仅要检查代码的逻辑是否正确,同时对于代码中依赖的其他数据源,获取到的元数据信息是否准确,至少字段的命名不会有大的问题。当代码预编译通过,发布上线后,还需要检测当前是否有资源支持任务启动,并且上游的消息队列是否是启动的状态。
实时的测试一直都是比较大的问题,它不像离线可以启动一个SQL任务看看结果,实时在每个阶段的输入和输出,是需要通过平台支持的日志打印功能来进行辅助的。很多时候我们会新建一个测试专用的topic来测试结果,但对于流量较大的线上任务而言,这种方式无法像离线区分Dev环境一样,能够对资源进行隔离,因而如果能够支持圈定数据的输入和打印输出,对于测试的效率而言无疑是最佳的。
最后要提到的是运维与监控能力。运维能力是指根据输入的RPS,或者是cu使用情况,或者是任务的整体延迟,提供相应的参数调优能力,通过参数来调整任务的执行情况。并且能够根据以上指标的变化,自定义相应的阈值,提供相应的告警能力,通过短信或者是消息工具的方式触达任务维护者。
实时与离线有一些不同的是,离线可以通过增加一个监控节点的方式,通过group
by判断数据是否重复,而实时任务则非常依赖Flink自身的一致性能力,因而发现和解决问题的成本更高。
其实做到运维这个环节,对人的要求其实是更高的。因为流批一体在运维上会带来一个好处,即实时任务和离线任务能够错峰执行,实时在白天压力大,而离线在晚上压力大。但同样的,这种方式对于维护者而言更加痛苦,因为不仅晚上要熬夜值班,白天同样不能休息,在大促期间甚至需要轮班来维护任务,可以说是“汇报一时爽,痛苦长相伴”。
从远处来看,流任务和批任务,在自身的机制上就存在非常大的差异,批程序面上的是特定时间内相对静态的数据,而流程序处理的则是change-log,虽然有可能数据在表结构层面,通过数据模型的设计来保持一致,但是在语义层面,其根本还是不一样的。这一点可能是最制约批流一体发展的问题,也是最难实现统一或者永远也不可能统一的。
综上,对于实时模型,开发工具需要将监控实时部分的能力进行补全,就像DWD层需要分别维护实时和离线两套架构一样,开发工具也需要分别维护两套架构的结果,因而现阶段的实时开发,还做不到降低维护和开发的成本,只能减轻其中部分环节的工作量。
<>|0xFF 流批一体的价值取决于业务方
以上讲了很长时间的实时模型,但从实际的效果上看,业务并不会感知到多么明显的技术变化,相反会有一种“面子工程”的感觉在里面。
当然,我并不否认实时的价值,在“搜广推”这三个技术占主导的领域内,作用还是很大的。但实时毕竟要比离线的内容,更加的难以理解,出现问题的排查成本也更高。这种复杂性使得我们在应对变化时,往往做不出有效的应对,就会变得特别被动。
因而,说一句事后的话,就是“实时的价值取决于业务方,而不是技术方”。只有业务对实时痛点强烈的场景下,我们做如此复杂的研究和应对,才能体现出自己的价值,更多的时候,是在“王婆卖瓜,自卖自夸”。有这种投入,还不如多招几个分析师更靠谱和实在。
本人之前的文章《天下数据,唯快不破》,重点强调了一个“快”字。但“天下熙熙皆为利来,天下攘攘皆为利往”,这个快更多的是在讲应对“变化”的快,而不是“技术”自己的快。
所以,为了以后的职业发展,我们要跟进实时技术的变化,但从自身的工作角度出发,如何应对业务的变化,才是自己要关心的课题。