非时间戳列上的Spark结构流窗口

时间:2018-09-12 07:49:21

标签: scala apache-spark spark-streaming aggregate-functions spark-structured-streaming

我正在获取以下形式的数据流:

+--+---------+---+----+
|id|timestamp|val|xxx |
+--+---------+---+----+
|1 |12:15:25 | 50| 1  |
|2 |12:15:25 | 30| 1  |
|3 |12:15:26 | 30| 2  |
|4 |12:15:27 | 50| 2  |
|5 |12:15:27 | 30| 3  |
|6 |12:15:27 | 60| 4  |
|7 |12:15:28 | 50| 5  |
|8 |12:15:30 | 60| 5  |
|9 |12:15:31 | 30| 6  |
|. |...      |...|... |

我有兴趣将窗口操作应用于xxx列,就像Spark Streaming中提供了带有时间戳的窗口操作一样,其中包含一些窗口大小和滑动步长。

在下面的带有窗口功能的groupBy中,lines表示一个流数据帧,其窗口大小为2,滑动步长为1。

val c_windowed_count = lines.groupBy(
  window($"xxx", "2", "1"), $"val").count().orderBy("xxx")

因此,输出应如下:

+------+---+-----+
|window|val|count|
+------+---+-----+
|[1, 3]|50 |  2  |
|[1, 3]|30 |  2  |
|[2, 4]|30 |  2  |
|[2, 4]|50 |  1  |
|[3, 5]|30 |  1  |
|[3, 5]|60 |  1  |
|[4, 6]|60 |  2  |
|[4, 6]|50 |  1  |
|...   |.. | ..  |

我尝试使用partitionBy,但Spark结构化流不支持它。

我正在使用Spark结构化流媒体2.3.1。

谢谢!

2 个答案:

答案 0 :(得分:3)

当前无法通过Spark结构化流以这种方式在非时间戳列上使用窗口。但是,您可以做的是xxx列转换为时间戳列,进行groupBycount,然后再转换回来。

from_unixtime可用于将1970年1月1日以来的秒数转换为时间戳。使用xxx列为秒,可以创建假时间戳记以在窗口中使用:

lines.groupBy(window(from_unixtime($"xxx"), "2 seconds", "1 seconds"), $"val").count()
  .withColumn("window", struct(unix_timestamp($"window.start"), unix_timestamp($"window.end")).as("window"))
  .filter($"window.col1" =!= 0)
  .orderBy($"window.col1")

上面,分组是在转换后的时间戳上进行的,下一行会将其转换回原始编号。由于前两行将是窗口[0,2](即仅在xxx等于1的行)上进行过滤,因此可以跳过。

上述输入的结果输出:

+------+---+-----+
|window|val|count|
+------+---+-----+
| [1,3]| 50|    2|
| [1,3]| 30|    2|
| [2,4]| 30|    2|
| [2,4]| 50|    1|
| [3,5]| 30|    1|
| [3,5]| 60|    1|
| [4,6]| 60|    2|
| [4,6]| 50|    1|
| [5,7]| 30|    1|
| [5,7]| 60|    1|
| [5,7]| 50|    1|
| [6,8]| 30|    1|
+------+---+-----+

答案 1 :(得分:0)

spark 2.2中的新功能是arbitrary-stateful-operations

一个用例正在管理用户会话,即“用户窗口”

scroll half way down this page to see an example

如果Shaido的聪明解决方案对您有用,那么我建议您坚持下去。对于更复杂的需求,任意状态操作似乎是必经之路。