目录
前言
实时数仓,难免会遇到join维表的业务。现总结几种方案,供各位看官选择:
其中中间留下两个问题,供大家思考,可留言一起讨论?
查找关联
查找关联就是在主流数据中直接访问外部数据(mysql,redis,impala ...)去根据主键或者某种关键条件去关联取值。
适合: 维表数据量大,但是主数据不大的业务实时计算。
缺点:数据量大的时候,会给外部数据源库带来很大的压力,因为某条数据都需要关联。
同步
访问数据库是同步调用,导致 subtak 线程会被阻塞,影响吞吐量
异步
AsyncIO 可以并发地处理多个请求,很大程度上减少了对 subtask 线程的阻塞。
状态编程,预加载数据到状态中,按需取
首先把维表数据初始化到state中,设置好更新时间,定时去把维表。
优点:flink 自己维护状态数据,"荣辱与共",不需要频繁链接外部数据源,达到解耦。
缺点:不适合大的维表和变化大的维表。
思考下:直接定义一个Map集合这样的优缺点是什么?可以留言说出自己的看法?
冷热数据
思想:先去状态去取,如果没有,去外部查询,同时去存到状态里面。StateTtlConfig 的过期时间可以设置短点。
优点:中庸取值方案,热备常用数据到内存,也避免了数据join相对过多外部数据源。
缺点:也不能一劳永逸解决某些问题,热备数据过多,或者冷数据过大,都会对state 或者 外部数据库造成压力。
广播维表
比如上面提到的字典表,每一个Task都需要这份数据,那么需要join这份数据的时候就可以使用广播维表。
「思考:」 如果把维表流也通过实时监控binlog到kafka,当维度数据发生变化时,更新放到状态中,这种方式,是不是更具有时效性呢?
(1)通过canal把变更binlog方式发送到kafka中。
(2)数据流定义成为广播流,广播到数据到主数据流中。
(3)定义一个广播状态存储数据,在主数据进行查找匹配,符合要求则join成功。
Temporal table Join(FlinkSQL与Flink Table API)
由于维表是一张不断变化的表(静态表只是动态表的一种特例)。那如何 JOIN 一张不断变化的表呢?如果用传统的 JOIN 语法来表达维表 JOIN,是不完整的。因为维表是一直在更新变化的,如果用这个语法那么关联上的是哪个时刻的维表呢?我们是不知道的,结果是不确定的。所以 Flink SQL 的维表 JOIN 语法引入了Temporal Table 的标准语法,用来声明关联的是维表哪个时刻的快照。
普通关联会一直保留关联双侧的数据,数据也就会一直膨胀,直到撑爆内存导致任务失败,Temporal Join则可以定期清理过期数据,在合理的内存配置下即可避免内存溢出。
Event Time Temporal Join
语法
使用事件时间属性(即行时间属性),可以检索过去某个时间点的键值。这允许在一个共同的时间点连接两个表。
举例
假设我们有一个订单表,每个订单都有不同货币的价格。为了将此表正确地规范化为单一货币,每个订单都需要与下订单时的适当货币兑换率相结合。
Processing Time Temporal Join
处理时间时态表连接使用处理时间属性将行与外部版本表中键的最新版本相关联。
根据定义,使用processing-time属性,连接将始终返回给定键的最新值。可以将查找表看作是一个简单的HashMap,它存储来自构建端的所有记录。这种连接的强大之处在于,当在Flink中无法将表具体化为动态表时,它允许Flink直接针对外部系统工作。
使用FOR SYSTEM_TIME AS OF table1.proctime表示当左边表的记录与右边的维表join时,只匹配当前处理时间维表所对应的的快照数据。
Lookup Table Join
Lookup Join 通常用于通过连接外部表(维度表)补充信息,要求一个表具有处理时间属性,另一个表使 Lookup Source ConneCTOr。
JDBC 连接器可以用在时态表关联中作为一个可 lookup 的 source (又称为维表)。用到的语法是 Temporal Joins 的语法。
总结
总体来讲,关联维表有四个基础的方式:
(1)查找外部数据源关联
(2)预加载维表关联(内存,状态)
(3)冷热数据储备(算是1和2的结合使用)
(4)维表变更日志关联(广播也好,其他方式的流关联也好)
「同时考虑:」 吞吐量,时效性,外部数据源的负载,内存资源,解耦性等等方面。
四种join方式不存在绝对的一劳永逸,更多的是针对业务场景在各指标上的权衡取舍,因此看官需要结合场景来选择适合的。
原文链接:














发表评论