例如,我经常在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)
}
}