在 Spark 中使用带有未定义帧的 WindowSpec 是否安全?



例如,我经常在Apache Spark中使用Window-Functions来计算累积总和。到目前为止,我从未指定过帧,因为输出是正确的。但最近我在博客(https://databricks.com/blog/2015/07/15/introducing-window-functions-in-spark-sql.html(中读到:

除了排序和分区之外,用户还需要定义 帧的起始边界、帧的结束边界和 框架的类型,即框架的三个组成部分 规范。

所以我想知道使用未指定的帧是否安全,例如:

import org.apache.spark.sql.expressions.Window
val df = (1 to 10000).toDF("i")
df
.select(
$"i",
sum($"i").over(Window.orderBy($"i")).as("running_sum1"),//unspecified frame
sum($"i").over(Window.orderBy($"i").rowsBetween(Window.unboundedPreceding, Window.currentRow)).as("running_sum2") // specified frame
)
.show()

+---+------------+------------+
|  i|running_sum1|running_sum2|
+---+------------+------------+
|  1|           1|           1|
|  2|           3|           3|
|  3|           6|           6|
|  4|          10|          10|
|  5|          15|          15|
|  6|          21|          21|
|  7|          28|          28|
|  8|          36|          36|
|  9|          45|          45|
| 10|          55|          55|
| 11|          66|          66|
| 12|          78|          78|
| 13|          91|          91|
| 14|         105|         105|
| 15|         120|         120|
| 16|         136|         136|
| 17|         153|         153|
| 18|         171|         171|
| 19|         190|         190|
| 20|         210|         210|
+---+------------+------------+

显然它们提供相同的输出,但是是否存在使用未指定帧的危险情况?顺便说一下,使用Spark 2.x。

是的,它是安全的。

查看 github 上Window对象的主分支的源代码,有以下注释(它在 2.3.0 分支中不存在(:

如果未定义排序,则默认使用无界窗口框架(rowFrame、unboundedPreceding、unboundedFollow(。定义排序时,默认情况下使用不断增长的窗口框架(rangeFrame、unboundedPreceding、currentRow(。

换句话说,当窗口上有排序时,即通过使用orderBy,框架上未指定的边界等于具有:

rowsBetween(Window.unboundedPreceding, Window.currentRow)

在不使用orderBy的情况下,默认值为一个完整的无界窗口:

rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)

进一步的调查表明,自从在相关的 github 分支 Spark 1.4.0 中引入窗口函数以来,就已经使用了这些默认值:

def defaultWindowFrame(
hasOrderSpecification: Boolean,
acceptWindowFrame: Boolean): SpecifiedWindowFrame = {
if (hasOrderSpecification && acceptWindowFrame) {
// If order spec is defined and the window function supports user specified window frames,
// the default frame is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
SpecifiedWindowFrame(RangeFrame, UnboundedPreceding, CurrentRow)
} else {
// Otherwise, the default frame is
// ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
SpecifiedWindowFrame(RowFrame, UnboundedPreceding, UnboundedFollowing)
}
}

最新更新