探索流批一体结构下的实时数仓

|0x00 越来越高的时效性要求

在数据仓库的建设过程里,我们一直秉承着“离线先行”的方针,因为离线的技术栈非常成熟,开发起来很快,同时监控工具也做的比较完善,出了问题能及时发现、及时处理。过去我们处理实时的需求,一般都会转化为准实时的模式,例如分钟级调度,但毕竟它不是完全的实时模式,不过绝大多数情况下,应对业务诉求也是足够了。

但随着Flink为代表的新一代框架的出现,很多业务已经不再满足于做准实时的开发了,完全实时化的数据流、面向实时做的数仓设计,就成为了数据和业务都关心的高价值项目。哪怕是没有实时的诉求,我们也会尽量去搞一套体系出来,不仅是为了赶时髦,也是为了给自己储备点未来的知识。实时化已经是大趋势,尽管实时不能完全代替离线场景,但常规的业务模式全部实时化,还是没有问题的。

但是,除了流量等场景,大部分复杂业务场景下,纯实时搞不定需求,比如反作弊。于是我们倾向于用流批一体的架构来做,先把业务做起来,再用实时链路去提升数据的时效性。流批一体最著名的,就是Lambda。Lambda架构是由Storm的作者Nathan Marz提出的一个实时大数据处理框架。Marz在Twitter工作期间开发了著名的实时大数据处理框架Storm,Lambda架构是其根据多年进行分布式大数据系统的经验总结提炼而成。Lambda架构的目标是设计出一个能满足实时大数据系统关键特性的架构,包括有:高容错、低延时和可扩展等。

除此之外,Kappa架构也是实时数仓的另一种方案,但是因为实时架构用到的资源,通常都比较贵,因此完全的Kappa架构,目前应用的不会太多,很长一段时间内,Lambda与Kappa两种方案会同时存在。

当然,实时数仓受制于架构的影响,也不可避免的需要同时写两套代码,而且因为解析方式的不同,Flink SQL难免要跟Hive SQL出现一些语法上的不一致,对于工程开发来讲,“要了亲命了”,我们在后面会专门讨论这个问题。

不论怎样,大数据处理的实时化,已经成为了一个潮流,而Flink也成为了实时计算行业的标准。

|0x01 数据计算、存储与查询引擎

实时数仓需要关心两种架构,一种是数据处理的架构,另一种则是数据库。

首先讲数据处理架构,Flink已经是实时计算行业事实上的标准。能够将业务开发实时化,只是Flink的一个起点,Flink最终的目的肯定是为了给用户提供彻底的实时离线一体化体验,而业务实时化只是一个起点,Flink的目标之一就是给用户提供实时离线一体化的用户体验。其实很多用户不仅需要实时的数据统计,为了确认运营或产品的策略的效果,用户同时还需要和历史(昨天,甚至是去年的同期)数据比较。而从用户的角度来看,原有的流、批独立方案存在一些痛点:

第一,就是一套代码,需要维护两遍,不仅语法不通,处理数据的思路也不同,很容易出错。

第二,就是数据处理链路冗余,事实上从第二天开始,数据就跟离线没什么不同了,而实时的资源通常又很贵,所以资源是会产生浪费的。

第三,就是数据口径不同,道理也很简单,架构不同,就是很容易出错。

Flink应对的方式,就是一套引擎,多种方式。

首先是SDK层,如Table/SQL,用户的业务逻辑只要开发一遍,就可以同时在流和批的两种场景下使用。

其次是执行引擎,提供了统一的DAG,用来描述数据处理流程Data Processing Pipeline。这个很关键,在业务逻辑执行前,都会先转化为DAG图,再转化成在分布式环境下执行的Task,以同时支持两种Shuffle方式。

更多的内容,这里不展开。

数据库的选型,即OLAP怎么搞,也是实时数仓另外一个关心的架构问题。过去海量数据的存储与计算,都是数仓技术需要考虑的问题,但随着技术的发展,不同形态的数据库技术发展蓬勃,比如ES、MongoDB、HBase、Kudu等出现,存储对于数据仓库技术而言,已经不再是非常难以解决的问题了,那么我们目前关心的,就是数据库能否支持数据的高速写入,以及能否实现高并发低延迟的查询响应。

为了能够更好的理解这个问题,我们把问题抽象成如下几个部分:

第一,高并发特性,目前的实时数据已经不再是给高管、运营看数的工具,随着数据运营深入人心,外部的商家等人群,都有访问实时数据的诉求,那么这时候高并发就是必须要考虑的问题。

第二,响应速度,大促变的越来越频繁,同时移动端看数据的需求也变得越来越多,甚至算法也开始要实时的数据源,大家都希望能够看到毫秒级的变化,也就是刷新一下页面数据就会变,那么数据库的响应速度,就显得非常重要了。

第三,处理时延,还是大促,我们需要考虑流量峰值下的数据处理能力,至少延迟不能增加太高。同时,实时数仓通常会带来资源的错峰计算,但其实随着云原生的普及,资源混布成为常态,这个特性已经带不来更多的收益了。

综上,其实我们需要考虑的是一种能够统筹OLTP和OLAP的数据库,学名叫作线事务处理/在线分析处理( HTAP: Hybrid Transactional/Analytical Processing),以分布式事务和水平弹性扩展为主要卖点的产品。或者,我们可以把此类框架称之为“数据湖”产品,因为我们的目标都是为了实时的处理和分析数据。

TiDB,或者是阿里的Hologres、ADB,都在朝着这条路去走。

但实时数据流处理有一个特殊的情况,即维表的处理,因为在实时数仓中,事实与维度必须明确区分,因为走的数据处理链路不同,而针对维度数据存储的数据库,就是HBase,K-V的查询场景,在这里依旧是一个强诉求。

因此,我们的架构设计,基本上可以统一在Kafka + Flink + TiDB/HBase这样的方案上。当然,随着未来技术不断迭代,相信实时数仓架构,会像离线那样完善起来,但这条路还需要走一段时间。当下的实时架构,很像当年离线蓬勃发展的情况。

|0x02 实时数仓的设计

回到本文的重心,实时数仓的设计,我们的思路依旧是遵循离线数仓的方式,即ODS、DWD、DWS、DIM、ADS进行区分。但不同的是,ODS统一采用消息队列来作为方案设计,因此ODS面向多数据源的设计就会弱化。其他各个层上,主要功能与离线类似,但设计有所不同。

类似的地方,DWD依旧作为明细数据来处理,DWS对数据域内数据做轻度汇总,ADS面向需求场景做统一设计。

实时数仓的挑战,主要体现在四个方面:

时效性挑战:实时数仓如果延迟大了,那么跟分钟级计算就没有本质区别,因此如何尽可能的快,就是实时数仓最大的挑战;

准确性挑战:离线数据有完整的质量保证体系,但这些在实时数仓还都是比较新的挑战,过去我们通过流批一体解决了多数据源带来的结果不一致性,但如果保证开发结果的准确,挑战依旧很大;

稳定性挑战:实时数仓与离线不同,数据计算过程中出现了错误,修补的成本非常高,甚至可能导致永久性的数据丢失;

灵活性挑战:离线数仓最大的特点,就是可以应对海量需求的开发挑战,而实时数仓一旦运行任务太多,不论对开发还是运维,挑战都是很大的,尤其是面对需求频繁变更的场景。

从数仓的规范上看,各层的变化如下:

ODS:由于流批一体统一了数据源,而且ODS原本就不对原始数据做处理,因此可以无需再建表,直接用数据源即可;

DWD:与离线类似,实时业务也需要构建事实明细表,但区别在于,离线方案中,我们为了提高明细表的使用便捷程度,往往会把一些常用的维度退化到事实表,但实时方案出于时效性的考虑,倾向于不退化或者只退化不会变的维度,也就是DWD更类似于范式建模;

DWS:这一层根据业务情况的不同,在实时数仓的建设策略上,差异比较大;通常情况下,离线建设DWS,是针对比较成熟的业务,将维度逐级上卷;这样做能够将逻辑进行收口,提高下游使用的复用率,但缺点就是做的比较厚重;如果实时业务不是那么复杂,那么就不建议将DWS建的很重;究其原因,汇总层的目的在于预计算、提升效率和保证指标的一致性,实时链路太长,容易造成高的延迟和比较大的资源消耗;

DIM:维表在实时计算中非常重要,也是重点维护的部分,维表需要实时更新,且下游基于最新的维表进行计算;

ADS:功能和用途不变。

在实际的开发过程中,数仓的分层设计,基于架构选择的不同,其实还是有区别的。

例如,DWD/DWS是基于事实进行统计,单业务过程,不带维度,而DIM维度数据存储在HBase中,在ADS层我们将两者融合在一起,进行计算。但如果考虑到维表更新的问题,那么其实ADS可能就不会存在,而是以视图的方式进行开发,进行查询的时候再进行计算,这就比较依赖数据库的能力了。

如果不涉及维度的更新,则是将DWD分成两个视图,一个映射到历史分区,一个映射到当前的分区,DWD表看起来就是一张表,下游可以直接做全量的计算,计算代码保持统一,那么计算结果也就保持了统一,统计之后再展示给前台。

上述表述可以看出,一些复杂的统计场景,如长周期累计统计,或者是多维度分析场景,受制于基础框架能力,不建议用实时方案来做。

本身在数仓场景中,ADS就是需要灵活设计的一层,因此实时数仓根据业务的不同,也需要做出灵活的变化。

因此,目前看还不存在统一的数仓结构方案,仍需要基于我们的架构选型,进行灵活的设计。

|0xFF 常见问题的解决

实时数仓,相较于离线数仓,有一些场景是需要做特殊考量和设计的。

第一个场景,是维表更新问题。

对于离线数仓而言,维度信息可以冗余到事实表中,因为数据都是一次性算好、一次性使用,这么做问题不大。但实时场景中,但维度信息发生变化后,如果依旧做冗余到事实表的设计,那么更新后的情况就无法被反应出来。因此,维度更新后,我们往往就需要用最新的维度数据,来刷新历史数据,但如果维度变化很频繁,那么这么做基本就是不可接受的。

基于如上的考虑,维度和事实,在实时数仓中,需要更严格的做区分,模型的设计更加倾向于范式建模,而非维度建模。如果数据库性能更得上,通常可以选择将维度数据和事实数据分开存储,最后查询的时候再做汇总。当然,维表更新,还需要考虑双流join等问题,挑战不可谓不大。

第二个场景,是数据质量的问题。

离线数据的质量保障,做的是比较好的,不仅有测试同学的辅助验证,还有平台提供的各种校验工具。但因为实时计算链路的特殊性,我们过去积累的经验和工具,往往无法复用在实时场景下,导致目前的实时质量依旧只能是人肉为主。

相对而言,离线的链路靠谱的多,我们就会考虑用离线的数据来修正实时的数据。而且,离线的数据还有完整的数据管理、数据权限、数据安全机制,这些都是实时数仓短时间内无法实现的、但非常有必要的功能。

第三个场景,是公共层建设的要求。

针对离线数仓,公共层有的时候设计很薄弱,是没有问题的,因为数据量总会要在某一层削减掉,这一层是在公共层还是应用层,问题都不是特别大。但实时数仓就需要考虑流量暴涨的情况,如果ADS直接访问明细数据层,那么响应延迟的增加就再所难免。

因此,实时数仓,对于业务场景的选择,就显得挑剔的多,公共层的本意是为了屏蔽底层变化对上游应用产生的影响,而我们要做的,就是在性能、成本、效率、质量之间取得平衡。

最后,流批一体,最大的意义,其实是用一套代码,实现两种计算模式,并不是完全的纯实时,这也是制约流批一体发展的最大问题。因此,未来发展方向,大概率会走向纯实时开发的链路,同时补全诸如质量保证、数据安全等方面的内容,这都需要比离线数仓更长的发展时间。

你可能感兴趣的:(探索流批一体结构下的实时数仓)