一起学习Spark(七)结构化流Structured Streaming编程指南(3)-Streaming Join操作

Structured Streaming支持一个流式DataSet/DataFrame与另一个流式或静态的DataSet/DataFrame进行Join操作。Join的结果将会是渐进性的增量改变的,类似于之前的流聚合的结果。在本节中,我们将探索在上述情况下支持哪种类型的连接(即内部连接、外部连接等)。在所有受支持的连接类型中,流DataSet/DataFrame连接的结果与流中包含相同数据的静态DataSet/DataFrame连接的结果完全相同。

stream-static Join

自从Spark 2.0引入以来,Structured Streaming已经支持流和静态DataFrame/Dataset之间的连接(内部连接和某种类型的外部连接)。这里有一个简单的例子。

val staticDf = spark.read. ...
val streamingDf = spark.readStream. ...

streamingDf.join(staticDf, "type")          // 内部连接一个静态DF
streamingDf.join(staticDf, "type", "right_join")  // 右外连接一个静态DF

注意stream-static连接是无状态的,所以不需要状态管理。需要注意的是有一些外连接不能被stream-static Join支持,这些会在博客的后面列出。

stream-stream Join

在Spark2.3中,Spark增加了对stream-stream连接的支持,也就是说,可以把两个流式DataSet/DataFrame连接起来,生成两个流式DS/DF连接的结果的挑战是在任何时间点DS的视图对于连接的两边都是不完整的,这使得查找输入之间的匹配非常困难。从一个输入流接收到的任何行都可能与将来从另一个输入流接收到的任何尚未接收到的行匹配。因此,对于这两个输入流,我们将过去的输入缓冲为流状态,这样我们就可以将未来的每个输入与过去的输入进行匹配,从而生成连接的结果。此外,与流聚合类似,Spark自动处理延迟的、无序的数据,并可以使用水印限制状态。下面看一下受支持的流连接的不同类型以及如何使用它们。

1)内连接与可选的水印

任何类型的列和任何类型的连接条件在内连接上都能被支持,然而,随着流的运行,流状态的大小将无限增长,因为所有过去的输入都必须保存,因为任何新的输入都可能与过去的任何输入相匹配。为了避免无界状态,必须定义额外的连接条件,使旧的输入无法与未来的输入匹配,因此可以从状态中清除。换句话说,必须在连接中执行以下附加步骤。

1.在两个输入上定义水印延迟,以便引擎知道输入延迟的程度(类似于流聚合)

2.定义一个跨两个输入的事件时间约束,这样引擎就可以知道什么时候不再需要一个输入的旧行(即不满足时间约束)。这个约束可以用两种方法中的一种来定义。a)时间范围连接条件 b)加入事件时间窗口

假设我们想要将一个广告显示流(当一个广告被显示时)与另一个用户点击广告流连接起来,以关联当显示导致可盈利的点击时。为了允许这个流-流连接中的状态清除,必须指定如下所示的水印延迟和时间约束。

a)水印延迟:就是说,在事件时间内,显示和相应的点击可能会延迟/无序,最多分别延迟2小时和3小时。

b)事件时间范围条件:例如,点击可以在相应的显示之后0秒到1小时的时间范围内发生。

代码示例:

import org.apache.spark.sql.functions.expr

val impressions = spark.readStream. ...
val clicks = spark.readStream. ...

// 在事件时间列上应用水印
val impressionsWithWatermark = impressions.withWatermark("impressionTime", "2 hours")
val clicksWithWatermark = clicks.withWatermark("clickTime", "3 hours")

// 使用事件时间约束进行连接
impressionsWithWatermark.join(
  clicksWithWatermark,
  expr("""
    clickAdId = impressionAdId AND
    clickTime >= impressionTime AND
    clickTime <= impressionTime + interval 1 hour
    """)
)

流内连接的语义保证与水印:

这类似于流聚合上的水印提供的保证。水印延迟为“2小时”,保证引擎不会删除任何延迟小于2小时的数据。但是延迟超过2小时的数据可能会被处理,也可能不会。

2)外连接与水印

对于内连接,水印+事件时间约束是可选的,而对于左右外连接,它们是必须指定的,这是因为在外部连接中可能生成NULL这样的结果,引擎必须知道输入行在将来什么时候不匹配。因此,必须指定水印+事件时间约束来生成正确的结果。因此,带有outer-join的查询与前面的广告的示例非常相似,只是会有一个额外的参数将其指定为outer-join。代码示例:

impressionsWithWatermark.join(
  clicksWithWatermark,
  expr("""
    clickAdId = impressionAdId AND
    clickTime >= impressionTime AND
    clickTime <= impressionTime + interval 1 hour
    """),
  joinType = "leftOuter"      // 取值可以是:"inner", "leftOuter", "rightOuter"
 )

流外连接的语义保证与水印:

对于水印延迟和是否删除数据,外部连接具有与内部连接相同的保证。

外部连接其他说明

关于如何生成外部结果,有几个重要的特征需要注意。

1.外部连接的延迟可能会导致生成NULL的结果,这取决于指定的水印延迟和时间范围条件。因为引擎必须等待那么长的时间来确保没有匹配,并且将来也不会有更多匹配。

2.在微批处理引擎的当前实现中,在微批处理的最后会进行水印的高级处理,下一个微批处理使用更新后的水印来清理状态并输出外部结果。因为我们只在需要处理新数据时触发微批处理,所以如果流中没有接收到新数据,外部结果的生成可能会延迟。简而言之,如果正在连接的两个输入流中的任何一个在一段时间内不接收数据,外部(两种情况,左或右)输出可能会延迟。

流操作中连接的支持矩阵

左输入右输入连接类型说明
StaticStatic所有类型支持所有类型,因为这个跟流没有任何数据上的关系,即便他可能会出现流式程序里。
StreamStatic内连接支持,无状态的
左外连接支持,无状态的
右外连接不支持
全外连接不支持
StaticStream内连接支持,无状态的
左外连接不支持
右外连接支持,无状态的
全连接不支持
StreamStream内连接支持,可以指定双方的水印+时间限制的状态清理
左外连接有条件支持,必须在右侧指定水印+时间限制,以获得正确的结果,可以地在左侧指定水印,以进行所有状态清理
右外连接有条件支持,必须在左侧指定水印+时间限制,以获得正确的结果,可以地在右侧指定水印,以进行所有状态清理
全连接不支持

关于Join操作的其他说明

1.连接可以级联,也就是说,可以执行df1.join(df2……).join(df3……).join(df4 ....)

2.从Spark 2.3开始,您只能在查询处于Append输出模式时使用Join操作。还不支持其他输出模式。

3.从Spark 2.3开始,在连接之前不能使用其他非map类操作。这里有一些不能使用的例子。

    1)不能在连接之前使用流聚合。

    2)无法在连接之前以update模式使用mapGroupsWithState和flatMapGroupsWithState。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值