Apache spark Spark函数别名-性能UDF 上下文

Apache spark Spark函数别名-性能UDF 上下文,apache-spark,apache-spark-sql,apache-spark-2.0,Apache Spark,Apache Spark Sql,Apache Spark 2.0,在我编写的许多sql查询中,我发现自己以完全相同的方式组合spark预定义函数,这通常会导致冗长和重复的代码,而我的开发人员本能地希望重构它 因此,我的问题是:是否有某种方法可以为函数组合定义某种类型的别名,而无需求助于UDF(出于perofmance原因避免使用UDF)——目标是使代码更清晰、更清晰。本质上,我想要的是类似于udfs的东西,但没有性能损失。此外,这些函数必须可以在spark.sql调用中可用的spark sql查询中调用。 例子 例如,假设我的业务逻辑是反转某个字符串并将其散列

在我编写的许多sql查询中,我发现自己以完全相同的方式组合spark预定义函数,这通常会导致冗长和重复的代码,而我的开发人员本能地希望重构它

因此,我的问题是:是否有某种方法可以为函数组合定义某种类型的别名,而无需求助于UDF(出于perofmance原因避免使用UDF)——目标是使代码更清晰、更清晰。本质上,我想要的是类似于
udfs
的东西,但没有性能损失。此外,这些函数必须可以在spark.sql调用中可用的spark sql查询中调用。

例子 例如,假设我的业务逻辑是反转某个字符串并将其散列如下:(请注意,这里的函数组合是不相关的,重要的是它是现有预定义spark函数的组合-可能有很多)

是否有一种方法可以声明
业务
功能,而无需支付使用
udf
的性能价格,从而允许将上面的代码重写为:

SELECT 
    business(person.name),
    business(person.some_information),
    business(person.some_other_information)
    ...
FROM person
我在spark文档和这个网站上搜索了很多,但没有找到实现这一点的方法,这对我来说很奇怪,因为这看起来是一种非常自然的需要,我不明白为什么你必须为定义和调用udf付出黑盒的代价

有没有一种方法可以声明一个业务函数而不必付出使用udf的性能代价

您不必使用
udf
,您可以扩展
Expression
类,或者对于最简单的操作-
UnaryExpression
。然后,您将不得不实现几个方法,现在我们开始。它本机集成到Spark中,此外还允许使用一些优势功能,如代码生成

在您的情况下,添加
业务
功能非常简单:

def business(column: Column): Column = {
  sha1(reverse(column))
}
必须可以从spark.sql调用中可用的spark sql查询中调用

这更棘手,但可以实现。
您需要创建自定义函数注册器:

import org.apache.spark.sql.catalyst.FunctionIdentifier
import org.apache.spark.sql.catalyst.expressions.Expression 

object FunctionAliasRegistrar {

val funcs: mutable.Map[String, Seq[Column] => Column] = mutable.Map.empty

  def add(name: String, builder: Seq[Column] => Column): this.type = {
    funcs += name -> builder
    this
  }

  def registerAll(spark: SparkSession) = {
    funcs.foreach { case (alias, builder) => {
      def b(children: Seq[Expression]) = builder.apply(children.map(expr => new Column(expr))).expr
      spark.sessionState.functionRegistry.registerFunction(FunctionIdentifier(alias), b)
    }}
  }
}
然后您可以按如下方式使用它:

FunctionAliasRegistrar
  .add("business1", child => lower(reverse(child.head)))
  .add("business2", child => upper(reverse(child.head)))
  .registerAll(spark) 

dataset.createTempView("data")

spark.sql(
  """
    | SELECT business1(name), business2(name) FROM data
    |""".stripMargin)
.show(false)
输出:

+--------------------+--------------------+
|lower(reverse(name))|upper(reverse(name))|
+--------------------+--------------------+
|sined               |SINED               |
|taram               |TARAM               |
|1taram              |1TARAM              |
|2taram              |2TARAM              |
+--------------------+--------------------+

希望这能有所帮助。

我想你可能已经回答了你自己的问题。这正是我所需要的。我刚刚测试了它(spark 2.4.2),如果您愿意使用Column类的构造函数,而不是使用
Column。在
Registeral
方法中应用
,那么您就不需要将
FunctionRegistrator
对象放入
org.apache.spark.sql
!(也许用这个改变来更新答案会很好,因为它使整个事情看起来不像黑客,更像是一个干净的扩展!非常感谢:)我没有注意到,将更新答案,谢谢。第二部分很好。
+--------------------+--------------------+
|lower(reverse(name))|upper(reverse(name))|
+--------------------+--------------------+
|sined               |SINED               |
|taram               |TARAM               |
|1taram              |1TARAM              |
|2taram              |2TARAM              |
+--------------------+--------------------+