Scala 仅使用Spark SQL API时广播变量的使用

Scala 仅使用Spark SQL API时广播变量的使用,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,在使用Spark RDD API时,我们可以使用广播变量来优化Spark分配不可变状态的方式 1) 广播变量如何在内部工作? 我的假设是: 对于用于在数据集上执行操作的每个闭包,它引用的所有变量都必须序列化、通过网络传输并与任务一起还原,以便可以执行闭包 注册如下广播变量时: import org.apache.spark.sql.functions.broadcast val result = dataframe.join(broadcast(allowedValuesDF), "m

在使用Spark RDD API时,我们可以使用广播变量来优化Spark分配不可变状态的方式

1) 广播变量如何在内部工作? 我的假设是: 对于用于在数据集上执行操作的每个闭包,它引用的所有变量都必须序列化、通过网络传输并与任务一起还原,以便可以执行闭包

注册如下广播变量时:

import org.apache.spark.sql.functions.broadcast
val result = dataframe.join(broadcast(allowedValuesDF), "mycol === allowedValues")
val broadcastVar=sc.broadcast(“你好,世界”)
返回的对象(
Broadcast[String]
)不保留对实际对象(“hello world”)的引用,只保留一些ID。 当广播变量句柄在上述闭包中被引用时,它将被序列化,就像其他变量一样——只是广播变量句柄本身不包含实际对象

稍后在目标节点上执行闭包时,实际对象(“hello world”)已传输到每个节点。当闭包到达调用
broadcastVar.value
的点时,广播变量句柄使用ID在内部检索实际对象

这个假设正确吗

2) 有没有办法在Spark SQL中利用这种机制? 假设我有一组允许的值

使用RDD-API时,我会为我的AllowedValue创建一个广播变量:

val broadcastAllowedValues=sc.broadcast(allowedValues)//broadcast[Set[String]]
filter(row=>broadcastAllowedValues.value.contains(row(“mycl”))
当然,在使用Spark SQL API时,我会使用
Column.isin
/
Column.isInCollection
方法:

dataframe.where(col(“mycl”).isInCollection(allowedvalue))
但我似乎无法通过这种方式获得广播变量的优势

另外,如果我将这段代码更改为以下内容:

val broadcastAllowedValues=sc.broadcast(allowedValues)//broadcast[Set[String]]
dataframe.where(col(“mycol”).isInCollection(allowedValues.value))
本部分:

col(“mycol”).isInCollection(allowedValues.value)
//更重要的是这一部分:
允许值
将已经在驱动程序上进行评估,从而生成一个新的
-对象。所以广播变量失去了它的优势。与第一个示例相比,它甚至会有一些开销

有没有一种方法可以使用Spark SQL API利用广播变量,或者在这些点上必须显式使用RDD-API

广播变量如何在内部工作

广播的数据被序列化并物理地移动到所有执行器。根据关于的文件,它说

“广播变量允许程序员在每台机器上缓存一个只读变量,而不是将其副本与任务一起发送。”

有没有办法在Spark SQL中利用这种机制

是的,有一种方法可以利用。Spark在加入大小数据帧时默认应用广播哈希连接

根据《学习火花-第二版》一书,它说:

“默认情况下,如果较小的数据集小于10MB,Spark将使用广播连接。此配置在
Spark.sql.autoBroadcastJoinThreshold
中设置;您可以根据每个执行器和驱动程序中的内存大小来减小或增大大小。”

在您的情况下,您需要将所有唯一的allowedValues列出到一个简单的数据框(称为
allowedeValuesDF
)中,其中只有一列(称为
allowValues
)并应用联接来筛选
数据框

大概是这样的:

import org.apache.spark.sql.functions.broadcast
val result = dataframe.join(broadcast(allowedValuesDF), "mycol === allowedValues")
实际上,您可以省略
广播
,因为默认情况下Spark将执行广播连接

编辑:

在Spark的更高版本中,您还可以在SQL语法中使用连接提示来告诉执行引擎要使用哪些策略。中提供了详细信息,下面提供了一个示例:

——广播连接的连接提示
从t1.key=t2.key上的t1内部连接t2中选择/*+广播(t1)*/*;

谢谢您的回答。我根据您的答案测试了我的过滤器的3种变体之间的时差(请参见:)实际上,第三种方法(
runWithBroadcastVariable
)是我的设置中最快的。连接只是稍微慢一点。但我不明白的是,为什么第一个(
runWithBroadcastVariable
实际上比其他两种变体都慢10倍。理论上,它不应该几乎等同于
runWithBroadcastVariable
?我知道这不是一种基准配置,但时间差很明显。你是对的,这看起来不像是一个合适的基准。我确信在某些情况下简单的广播作为散列连接速度更快,特别是对于simpöe number rangers,散列不会有任何改进,只会增加一些成本。我也不熟悉spark.run
度量以及它实际考虑的内容。也许可以看看数据帧的
解释图这张
explain
图可能会显示
runWithInCollection
需要在分区之间进行大量的数据洗牌,这是非常昂贵的。实际上,
runWithInCollection
根本不需要任何洗牌。但它通常会创建某个类的动态实现,而这是不可行的k如果集合太大(10000个整数似乎已经太大而无法编译)。例如,仅使用500个AllowedValue时,
runWithInCollection
实际上是所有3个集合中速度最快的。请参见: