漫画趣解Flink实时数仓~
我是Flink,最近我抑郁了~
1 引子(搬橡果)
过冬了,我和小伙伴灰灰开始屯年货。
今年劳动了大半年,我们收获了整整一车的橡果。众所周知,我们小松鼠们都喜欢把这些心爱的橡果放到储藏室。
于是今天起了个大早,开始搬运这些橡果。
不一会,灰灰突然对我说想要吃一颗昨天摘的灰色小橡果。
我望了望眼前堆积如山的年货,苦恼的摸了摸脑袋:等我搬到了那颗再给你。
灰灰很不开心,嘴里嘟囔着:为啥昨天不能一摘下来我们就搬呢?
我解释道: 我们每年都是攒够一车才一起搬的呀?
看着一边气鼓鼓的灰灰,我放缓了搬运的速度~
抬头望着高高的橡果堆叹了口气。一边搬运,一边翻找他要的那颗小橡果。。。
今天怕是搬不完了~
2 慢 OR 快?
总结下,在故事中我们遇到了几个小烦恼:
每次都是攒了整车橡果才开始搬运,无法 及时
拿到想要的灰色小橡果就算我 实时
搬运。之后再要其他小橡果,我还是不能快速找到
,完全记不住之前拿过哪些?放到了哪里?
关键词:速度慢、体量大、及时性差、 快速查找、可回溯。。
借由这个小故事,回归到本文主题。
这些关键词也是企业实时数仓建设
中常遇到的一些难点和诉求。
2.1 企业实时数仓建设诉求
大多数企业面临数据源多、结构复杂的问题,为了更好的管理数据和赋能价值,常常会在集团、部门内进行数仓建设。
其中一般初期的数仓开发流程大致如下:
获取数据源,进行数据清洗、扩维、加工,最终输出业务指标 根据不同业务,重复进行上述流程开发,即 烟囱式
开发。
可想而知,随着业务需求的不断增多,这种烟囱式
的开发模式会暴露很多问题:
代码耦合度高 重复开发 资源成本高 监控难
为此大量企业的数据团队开始着手数仓规划,对数据进行分层。
数据规整为层级存储,每层独立加工。整体遵循由下向上建设思想,最大化数据赋能。
数据源: 分为 日志数据
和业务数据
两大类,包括结构化和非结构化数据。数仓类型:根据及时性分为 离线
数仓和实时
数仓技术栈: 采集(Sqoop、Flume、CDC) 存储(Hive、Hbase、Mysql、Kafka、数据湖) 加工(Hive、Spark、Flink) OLAP查询(Kylin、Clickhous、ES、Dorisdb)等。
2.2 稳定的离线数仓
业务场景
要求每天出一个当日用户访问PV、UV流量报表,结果输出到业务数据库
早期规划中,在数据实时性要求不高的前提下,基本一开始都会选择建设离线数仓。
1) 技术实现
使用Hive作为数据存储、计算技术栈 编写数据同步脚本,抽取数据到Hive的ODS层中 在Hive中完成dwd清洗加工、维度建模和dws汇总、主题建模 依赖调度工具(dophinScheduler)自动 T+1调度 olap引擎查询分析、报表展示
2) 优缺点
配合调度工具,能够自动化实现T+1的数据采集、加工等全流程处理。技术栈 简单
易操作Hive存储性能高、适合交互式查询 计算速度受Hive自身限制,可能因参数和数据分布等差异造成不同程度的数据 延迟
3) 改良
既然我们知道了Hive的运算速度比较慢,但是又不想放弃其高效的存储和查询功能。
那我们试试换一种计算引擎: Spark。
整体流程不变,主要是在ods->dwd->dws层的数据加工由Spark负责。效果是显而易见的,比Hive计算快了不少。
注意Spark是内存级计算引擎,需要合理规划内存大小,防止出现OOM(内存泄露)。
目前两种离线数仓均完美的实现了业务需求。领导第二天一看报表统计,结果皆大欢喜~
现在考虑换一种场景:不想等到第二天才能看到结果,要求实时展示指标,此时需要建设实时数仓。
3 冗余 OR 可回溯 ?
业务场景
实时统计每秒用户访问PV、UV流量报表,结果输出到业务数据库,并支持历史数据回看
既然要求达到实时效果,首先考虑优化加工计算过程。因此需要替换Spark,使用Flink计算引擎。
在技术实现方面,业内常用的实时数仓架构分为两种:Lambda
架构和Kappa
架构。
3.1 Lambda架构
顾名思义,Lambda架构保留实时、离线两条处理流程,即最终会同时构建实时数仓和离线数仓。
1) 技术实现
使用Flink和Kafka、Hive为主要技术栈 实时技术流程。通过实时采集程序同步数据到Kafka消息队列 Flink实时读取Kafka数据,回写到 kafka ods
贴源层topicFlink实时读取Kafka的ods层数据,进行实时清洗和加工,结果写入到 kafka dwd
明细层topic同样的步骤,Flink读取dwd层数据写入到 kafka dws
汇总层topic离线技术流程和前面章节一致 实时olap引擎查询分析、报表展示
2) 优缺点
两套技术流程,全面保障实时性和历史数据完整性 同时维护两套技术架构,维护成本高,技术难度大 相同数据源处理两次且存储两次,产生大量数据冗余和操作重复 容易产生数据不一致问题
3) 改良
针对相同数据源被处理两次
这个点,对上面的Lambda架构进行改良。
通过将实时技术流的每一层计算结果定时刷新到离线数仓中,数据源读取唯一。大幅减少了数据的重复计算,加快了程序运行时间。
总结: 数据存储、计算冗余;历史数据可追溯
3.2 Kappa架构
为了解决上述模式下数据的冗余存储和计算的问题,同时降低技术架构复杂度,这里介绍另外一种模式: Kappa架构。
1) 技术实现
使用Flink和Kafka为主要技术栈 实时技术流和Lambda架构保持一致 不再进行离线数仓构建 实时olap引擎查询分析、报表展示
2) 优缺点
单一实时数仓,强实时性,程序性能高 维护成本和技术栈复杂度远远低于Lambda架构 源头数据仅作为实时数据流被计算、存储,数据仅被处理一次。 数据回溯难。依赖Kafka存储,历史数据会丢失 olap查询难。Kafka需要引入其他对接工具实现olap查询,Kafka天生不适合olap分析。
总结: 数据存储计算仅一次;历史数据回溯难
总体而言,第一种Lambda架构虽然有诸多缺点,但是具备程序稳健性和数据完整性,因此在企业中用的会比较多。
相反Kappa架构用的比较少。因为Kappa架构仅使用Kafka作为存储组件,需要同时满足数据完整性和实时读写,这明显很难做到。
Kappa架构的实时数仓道路将何去何从?
4 数据湖&实时数仓
我们明白,Kafka的定位是消息队列,可作为热点数据的缓存介质,对于数据查询和存储其实并不适合。
如果能够找到一个替代Kafka的实时数据库就好了。。
预期要求
1)能够支持数据回溯和数据更新
2)实现数据批流读写,支持实时接入
4.1 数据湖技术
近些年,随着数据湖技术的兴起,仿佛看到了一丝希望。
目前市场上最流行的数据湖为三种: Delta、Apache Hudi和Apache Iceberg。
其中Delta和Apache Hudi
对于多数计算引擎的支持度不够,特别是Delta完全是由Spark衍生而来,不支持Flink。
其中的Iceberg,Flink是完全实现了对接机制。看看其具备的功能:
基于 快照
的读写分离
和回溯流批统一
的写入和读取非强制绑定计算引擎 支持 ACID
语义支持表、分区的 变更
特性
4.2 kappa架构升级
因此考虑对Kappa架构进行升级,使用Flink + Iceberg(Hudi)技术架构,可以解决Kappa架构中的一些问题。
存储介质由Kafka换成Iceberg(Hudi),其余技术栈保持不变 Flink读取源头Kafka数据,结果存储到Iceberg ods层 继续执行后续的ods->dwd->dws层计算、结果存储 Iceberg(Hudi)支持流批一体查询,过程中支持olap查询 实时olap引擎查询分析、报表展示
目前Flink社区关于Iceberg(Hudi)的建设已经逐渐成熟,其中很多大厂开始基于Flink + Iceberg(Hudi)打造企业级实时数仓。
更多实时数仓问题,可以咨询我的wx: youlong525.
5 电商零售实时数仓实战
纸上得来终觉浅,这里简单介绍一下老兵之前做过的实时数仓案例。
使用的技术栈可能有点老,主要探讨下建设思路。
5.1 技术架构
系统整体采用Flink + Spark + Kafka为主要技术栈,由底向上构建电商零售实时数仓,最终提供统一的数据服务。
1)底层使用Flink CDC
技术实时抽取源数据,包括业务系统
和第三方埋点
数据(客户中心、营销中心、销售中心)。
// data格式
{
"data": [
{
"id": "13",
"order_id": "6BB4837EB74E4568DDA7DC67ED2CA2AD9",
"order_code": "order_x001",
"price": "135.00"
}
]
}
// flink cdc (示例)
CREATE TABLE order_detail_table (
id BIGINT,
order_id STRING,
order_code STRING,
price DECIMAL(10, 2)
) WITH (
'connector' = 'kafka',
'topic' = 'order_binlog',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'group001',
'canal-json.ignore-parse-errors'='true'
);
2)数据源经过计算引擎和决策引擎转换,构建实时明细
、实时轻度汇总
、实时高度汇总
模型,即对应数仓分层: DWD、DWS、ADS层。
初步规划技术栈为Spark Streaming + Kafka。后期因实时性要求,改为Flink + Kafka,满足秒级响应。
3)构建完实时数仓模型后,数据转存至存储介质
。包括ES、Redis、Mysql、Kafka等,并最终向外提供API共享服务访问。
// 存储介质API服务
val esServices = new EsHandler[BaseHandler](dataFlows)
val kafkaServices = new KafkaHandler[BaseHandler](dataFlows)
val redisServices = new RedisHandler[BaseHandler](dataFlows)
val jdbcServices = new JDBCHandler[BaseHandler](dataFlows)
esServices.handle(args)
kafkaServices.handle(args)
redisServices.handle(args)
jdbcServices.handle(args)
4)最终向外提供API服务,为企业的智能推荐
、会员画像
、数据挖掘
、营销大屏
等应用服务提供数据支撑。
5.2 数据流程
整体从上而下,数据经过采集
-> 数仓明细加工
、汇总
-> 应用
步骤,提供实时数仓服务。
这里列举用户分析的数据流程和技术路线:
采集用户行为数据,统计用户曝光点击信息,构建用户画像。
6 实时数仓的优化与总结
1)实时数仓到底是Lambda架构还是Kappa架构好?
这个没有标准答案。这里给个建议:一般中小型
项目或需要保证历史数据
的完整性,建议使用Lambda架构构建,提供离线流程保障。目前Kappa架构用的不多,受场景和实时技术栈因素影响。
2)数据丢失怎么办?
如果是数据源丢失,可以重新消费
(offset位置);如果是Flink窗口数据延迟:可手动调大延迟时间
,延缓窗口关闭;或者使用侧输出流
保存延迟数据,再合并处理;也可以延迟数据写入存储介质
,后续统一处理。
3)实时计算中数据重复怎么办?
内存去重:数据量不大建议使用flink的 state
结构或者借助bitmap
结构稍微大点可以用 布隆过滤器
或hyperlog(借助工具)外部介质(redis或hbase)设计好 key
实现自动去重,存在存储成本
4)如何进行多条实时流JOIN
Flink内部提供JOIN算子操作,包括JOIN、window JOIN、Interval Join和connect等算子,详情请查看我的Flink双流JOIN文章。
5)实时任务和离线任务怎么调度
给YARN任务打上标签,将离线和实时分开,提交作业时指定Lable;同时调整Yarn的调度参数,合理分配多container执行。
--END--
非常欢迎大家加我个人微信,有关大数据的问题我们在群内一起讨论
长按上方扫码二维码,加我微信,拉你进群