“伴随着实时化浪潮的发展和深化,Flink已逐步演进为实时流处理的领军技术和事实标准。Flink一方面持续优化其流计算核心能力,不断提高整个行业的流计算处理标准,另一方面沿着流批一体的思路逐步推进架构改造和应用场景落地,但是,随着计算流批统一的逐渐完善的同时,Flink存储的流批统一缺陷显得尤为捉襟见肘”
Flink这几年一直在反复强调流批一体,即:使用同一套API、同一套开发范式来实现大数据的流计算和批计算,进而保证处理过程与结果的一致性。
但是,之前Flink一直强调的仅仅是计算层的流批一体,至于流批一体,还有哪些层面呢?
数据集成流批一体:离线与实时是否使用统一数据采集方式;如统一通过CDC或者OGG将数据实时捕获推送到kafka,批与流在从kafka中消费数据,载入明细层。
数据存储流批一体:离线与实时数据是否统一分层、统一存储;兼容数据的一致性和实时性。
处理逻辑流批一体:流与批处理是否使用统一SQL语法或者ETL组件,再通过底层分别适配流与批计算引擎,保证数据口径的一致性。
计算引擎流批一体:流与批使用同一套计算引擎,从根本上避免同一个处理逻辑流批两套代码问题。
其实,在解决了计算层的问题之后,掣肘的便是数据存储。目前,很多实时数仓中,实时链路采用kafka之类的消息队列,但是中间消息队列数据不利于分析。如果用户想要分析实时链路中一个明细层的数据,其实非常不方便,很多用户目前采用的办法可能是先把这个明细层中的数据导出来,比如导到Hive做离线分析,但这个时效性会大幅下降,或者为了加速查询,把数据导入到其他OLAP引擎中,但这又会增加系统复杂度,且数据一致性同样很难保证。
截止到目前,整个行业还没有完整的一站式解决计算引擎和数据存储流批一体的技术方案,这对当前流式计算引擎提出了更高的要求和挑战,不过庆幸的是,flink已经在这方面布局,在下一个迭代版本flink1.5中,被定义为流批一体的数据存储系统的FlinkDynamicTable即将面世。
毫无疑问,这对整个行业是巨大的创新。
提到flink发展存储系统,我们不得不先回顾传统大数据架构的演化过程,以史为镜,才能发现存储计算的一体的重要性和紧迫性。
一迭代中的数据仓库,磕磕绊绊
实时数仓的架构,从经典的主题建模,到维度建模,再到hadoop体系,后面的lamda架构,kappa架构,在逐步完善,但一直没有形成完整的解决方案。
1)离线数仓
使用hadoop平台的hive做数据仓库,报表层数据保存在mysql中,使用tableau做报表系统,这样不用担心存储问题、计算速度也大大加快了。在此基础上,提供hue给各个部门使用,这样简单的取数工作可以由运营自己来操作,使用presto可以做mysql、hive的跨库查询,大大提升了查询效率。
2)Lambda架构
为了计算一些实时指标,就在原来离线数仓的基础上增加了一个实时计算的链路,并对数据源做流式改造(即把数据发送到消息队列),实时计算去订阅消息队列,直接完成指标增量的计算,推送到下游的数据服务中去,由数据服务层完成离线实时结果的合并。
需要注意的是流处理计算的指标批处理依然计算,最终以批处理为准,即每次批处理计算后会覆盖流处理的结果(这仅仅是流处理引擎不完善做的折中)。
Lambda架构整合离线计算和实时计算,融合不可变性(Immunability),读写分离和复杂性隔离等一系列架构原则,可集成Hadoop,Kafka,Storm,Spark,Hbase等各类大数据组件。
同样的需求需要开发两套一样的代码,这是Lambda架构最大的问题,两套代码不仅仅意味着开发困难(同样的需求,一个在批处理引擎上实现,一个在流处理引擎上实现,还要分别构造数据测试保证两者结果一致),后期维护更加困难,比如需求变更后需要分别更改两套代码,独立测试结果,且两个作业需要同步上线。
资源占用增多:同样的逻辑计算两次,整体资源占用会增多(多出实时计算这部分)。
实时链路和离线链路计算结果容易让人误解,昨天看到的数据和今天看到的数据不一致**。
下游处理复杂,需要整合实时和离线处理结果,这一部分往往是我们在呈现给用户之前就完成了的。
3)Kappa架构
再后来,实时的业务越来越多,事件化的数据源也越来越多,实时处理从次要部分变成了主要部分,架构也做了相应调整,出现了以实时事件处理为核心的Kappa架构。当然这不要实现这一变化,还需要技术本身的革新——Flink,Flink的出现使得Exactly-Once和状态计算成为可能,这个时候实时计算的结果保证最终结果的准确性。
Lambda架构虽然满足了实时的需求,但带来了更多的开发与运维工作,其架构背景是流处理引擎还不完善,流处理的结果只作为临时的、近似的值提供参考。后来随着Flink等流处理引擎的出现,流处理技术很成熟了,这时为了解决两套代码的问题,LickedIn的JayKreps提出了Kappa架构。
Kappa架构可以认为是Lambda架构的简化版(只要移除lambda架构中的批处理部分即可)。在Kappa架构中,需求修改或历史数据重新处理都通过上游重放完成。
存在的问题:Kappa架构最大的问题是流式重新处理历史的吞吐能力会低于批处理,但这个可以通过增加计算资源来弥补。
4)混合架构
在真实的场景中,很多时候并不是完全规范的Lambda架构或Kappa架构,可以是两者的混合,比如大部分实时指标使用Kappa架构完成计算,少量关键指标(比如金额相关)使用Lambda架构用批处理重新计算,增加一次校对过程。
Kappa架构并不是中间结果完全不落地,现在很多大数据系统都需要支持机器学习(离线训练),所以实时中间结果需要落地对应的存储引擎供机器学习使用,另外有时候还需要对明细数据查询,这种场景也需要把实时明细层写出到对应的引擎中。
还有就是Kappa这种以实时为主的架构设计,除了增加了计算难度,对资源提出了更改的要求之外,还增加了开发的难度,所以才有了下面的混合架构,可以看出这个架构的出现,完全是出于需求和现状考虑的。
混合架构在解决了部分业务问题的同时,也带了架构的复杂性,在计算引擎及存储介质上,存在多元性,那么不管是学习成本还是开发成本以及后期的维护成本,都是指数级的增长,未必是一种最优的选择。
同样,混合架构支持实时入湖、入湖实时增量分析,但这些场景的实时性大打折扣,因为数据湖存储格式本质还是Mini-Batch,实时计算在混合架构中退化到Mini-Batch模式。毫无疑问,这对实时性要求很高的业务是很大的灾难。
二Flink流批一体的架构演进,带来无限想象
Flink流批一体在技术架构演进和落地应用两方面都有了新进展。
技术演进层面,Flink流批一体API和架构改造已经完成,在原先的流批一体SQL基础上,进一步整合了DataStream和DataSet两套API,实现了完整的Java语义层面的流批一体API,架构上做到了一套代码可同时承接流存储与批存储。
在年10月发布的Flink1.14版本中,已经可以支持在同一个应用中混合使用有界流和无界流:Flink现在支持对部分运行、部分结束的应用(部分算子已处理到有界输入数据流的末端)做Checkpoint。此外,Flink在处理到有界数据流末端时会触发最终Checkpoint,以确保所有计算结果顺利提交到Sink。
而批执行模式现在支持在同一应用中混合使用DataStreamAPI和SQL/TableAPI(此前仅支持单独使用DataStreamAPI或SQL/TableAPI)。
此外,Flink更新了统一的Source和SinkAPI,开始围绕统一的API整合连接器生态。新增的混合Source可在多个存储系统间过渡,实现诸如先从AmazonS3中读取旧的数据再无缝切换到ApacheKafka这样的操作。
三FlinkCDC,流批一体走向成熟的助推器
数据集成、不同数据源之间的数据同步对于很多团队来说是刚需,但传统方案往往复杂度太高且时效性不好。传统的数据集成方案通常是离线数据集成和实时数据集成分别采用两套技术栈,其中涉及很多数据同步工具,比如Sqoop、DataX等,这些工具要么只能做全量要么只能做增量,开发者需要自己控制全增量的切换,配合起来比较复杂。
这个时候,flinkcdc粉墨登场,对变更数据实时捕获。基于Flink的流批一体能力和FlinkCDC,只需要写一条SQL,就可以做到先全量同步历史数据,再自动断点续传增量数据,实现一站式数据集成。全程无需用户判断和干预,Flink能自动完成批流之间的切换并保证数据的一致性。
FlinkCDCConnectors作为一个独立的开源项目,从去年7月份开源以来,一直保持相当高速的发展,平均两个月一个版本。目前FlinkCDC版本已经更新到2.1版本,并完成了很多主流数据库的适配,比如MySQL、PostgreSQL、MongoDB、Oracle等,更多数据库如TiDB、DB2等的对接工作也在进行中。可以看到已经有越来越多企业在自己的业务场景中使用FlinkCDC。
四Flink流式数仓,让存算一体的不再是远方
Flink流批一体的理念可以在上述场景下得到充分应用。Flink可以让当前业界主流数仓架构再进阶一层,实现真正端到端全链路的实时化分析能力,即:当数据在源头发生变化时就能捕捉到这一变化,并支持对它做逐层分析,让所有数据实时流动起来,并且对所有流动中的数据都可以实时查询。再借助Flink完备的流批一体能力,使用同一套API就可以同时支持灵活的离线分析。这样一来,实时、离线以及交互式查询分析、短查询分析等,就可以统一成一整套解决方案,成为理想中的“流式数仓(StreamingWarehouse)”。
流式数仓(StreamingWarehouse)更准确地说,其实是“makedatawarehousestreaming”,就是让整个数仓的数据全实时地流动起来,且是以纯流的方式而不是微批(mini-batch)的方式流动。目标是实现一个具备端到端实时性的纯流服务(StreamingService),用一套API分析所有流动中的数据,当源头数据发生变化,比如捕捉到在线服务的Log或数据库的Binlog以后,就按照提前定义好的Query逻辑或数据处理逻辑,对数据进行分析,分析后的数据落到数仓的某一个分层,再从第一个分层向下一个分层流动,然后数仓所有分层会全部流动起来,最终流到一个在线系统里,用户可以看到整个数仓的全实时流动效果。
在这个过程中,数据是主动的,而查询是被动的,分析由数据的变化来驱动。同时在垂直方向上,对每一个数据明细层,用户都可以执行Query进行主动查询,并且能实时获得查询结果。此外,它还能兼容离线分析场景,API依然是同一套,实现真正的一体化。
目前业界还没有这样一个端到端全流式链路的成熟解决方案,虽然有纯流的方案和纯交互式查询的方案,但需要用户自己把两套方案加起来,必然会增加系统的复杂性,如果要再把离线数仓方案也加进来,系统复杂性问题就更大了。流式数仓要做的是在实现高时效性的同时,不进一步提高系统复杂性,让整个架构对于开发和运维人员来说都是非常简洁的。
当然,流式数仓是终态,要达成这个目标,Flink需要一个配套的流批一体存储支持。其实Flink本身有内置的分布式RocksDB作为State存储,但这个存储只能解决任务内部流数据状态的存储问题。
流式数仓需要一个计算任务之间的表存储服务:第一个任务将数据写进去,第二个任务就能从它实时地再读出来,第三个任务还能对它执行用户的Query分析。因此Flink需要再扩展出一个跟自身理念配套的存储,从State存储走出来,继续向外走。为此,Flink社区提出了新的DynamicTableStorage,即具备流表二象性的存储方案。
FlinkDynamicTable可以理解为一套流批一体的存储,并无缝对接FlinkSQL。原来Flink只能读写像Kafka、HBase这样的外部表,现在用同一套FlinkSQL语法就可以像原来创建源表和目标表一样,创建一个DynamicTable。
流式数仓的分层数据可以全部放到FlinkDynamicTable中,通过FlinkSQL就能实时地串联起整个数仓的分层,既可以对DynamicTable中不同明细层的数据做实时查询和分析,也可以对不同分层做批量ETL处理。
从数据结构上看,DynamicTable内部有两个核心存储组件,分别是FileStore和LogStore。顾名思义,FlieStore存储Table的文件存储形式,采用经典的LSM架构,支持流式的更新、删除、增加等;同时,采用开放的列存结构,支持压缩等优化;它对应FlinkSQL的批模式,支持全量批式读取。而LogStore存储的是Table的操作记录,是一个不可变更序列,对应FlinkSQL的流模式,可以通过FlinkSQL订阅DynamicTable的增量变化做实时分析,目前支持插件化实现。
未来,利用FlinkCDC、FlinkSQL、FlinkDynamicTable就可以构建一套完整的流式数仓,实现实时离线一体化及对应计算存储一体化的体验。那便是大数据技术,flink技术发展的又一个精进高度。