我在Spark中运行了一个UDF(在EMR上运行),用scala编写,使用uaparser库为scala(uap-scala)解析来自用户代理的设备。在小型设备上工作时,它可以正常工作(5000行),但在较大的设备(2M)上运行时效果非常慢。 我尝试收集Dataframe以列出并在驱动程序上循环它,这也非常慢,是什么让我相信UDF在驱动程序而不是工作程序上运行
这是udf代码:
def calcDevice(userAgent: String): String = {
val userAgentVal = Option(userAgent).getOrElse("")
Parser.get.parse(userAgentVal).device.family
}
val calcDeviceValUDF: UserDefinedFunction = udf(calcDevice _)
用法:
.withColumn("agentDevice", udfDefinitions.calcDeviceValUDF($"userAgent"))
由于 尼尔
答案 0 :(得分:2)
问题在于在UDF中实例化构建器。解决方案是在udf之外创建对象并在行级使用它:
val userAgentAnalyzerUAParser = Parser.get
def calcDevice(userAgent: String): String = {
val userAgentVal = Option(userAgent).getOrElse("")
userAgentAnalyzerUAParser.parse(userAgentVal).device.family
}
val calcDeviceValUDF: UserDefinedFunction = udf(calcDevice _)
答案 1 :(得分:1)
我们遇到了Spark作业挂起的同一问题。我们要做的另一件事是使用广播变量。经过所有更改后,此UDF实际上非常慢,因此您的行驶里程可能会有所不同。另一项警告是获取 SparkSession ;我们在Databricks中运行,如果 SparkSession 不可用,它将崩溃。如果您需要继续工作,则必须处理该失败案例。
object UDFs extends Serializable {
val uaParser = SparkSession.getActiveSession.map(_.sparkContext.broadcast(CachingParser.default(100000)))
val parseUserAgent = udf { (userAgent: String) =>
// We will simply return an empty map if uaParser is None because that would mean
// there is no active spark session to broadcast the parser.
//
// Also if you wrap the potentially null value in an Option and use flatMap and map to
// add type safety it becomes slower.
if (userAgent == null || uaParser.isEmpty) {
Map[String, Map[String, String]]()
} else {
val parsed = uaParser.get.value.parse(userAgent)
Map(
"browser" -> Map(
"family" -> parsed.userAgent.family,
"major" -> parsed.userAgent.major.getOrElse(""),
"minor" -> parsed.userAgent.minor.getOrElse(""),
"patch" -> parsed.userAgent.patch.getOrElse("")
),
"os" -> Map(
"family" -> parsed.os.family,
"major" -> parsed.os.major.getOrElse(""),
"minor" -> parsed.os.minor.getOrElse(""),
"patch" -> parsed.os.patch.getOrElse(""),
"patch-minor" -> parsed.os.patchMinor.getOrElse("")
),
"device" -> Map(
"family" -> parsed.device.family,
"brand" -> parsed.device.brand.getOrElse(""),
"model" -> parsed.device.model.getOrElse("")
)
)
}
}
}
您可能还想玩 CachingParser 的大小。
答案 2 :(得分:0)
如果问题中缺少Parser.get.parse
,则只能判断udf
部分。
对于效果,您可以删除Option
:
def calcDevice(userAgent: String): String = {
val userAgentVal = if(userAgent == null) "" else userAgent
Parser.get.parse(userAgentVal).device.family
}