Scala 仅使用Spark SQL API时广播变量的使用
在使用Spark RDD API时,我们可以使用广播变量来优化Spark分配不可变状态的方式 1) 广播变量如何在内部工作? 我的假设是: 对于用于在数据集上执行操作的每个闭包,它引用的所有变量都必须序列化、通过网络传输并与任务一起还原,以便可以执行闭包 注册如下广播变量时: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
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个集合中速度最快的。请参见: