SparkSQL 之 UDF、UDAF 函数代码实现

摘要:本文从 UDF/UDAF/UDTF 三大函数类型、两种注册方式、弱类型 vs 强类型 UDAF、Aggregator 生命周期、性能陷阱五个维度,配合 2 张架构图 + 完整代码,彻底掌握 SparkSQL 自定义函数实现。

关键词:UDF, UDAF, UDTF, Aggregator, functions.udf, spark.udf.register


一、三大函数类型

css 复制代码
UDF  一对一: 1 行 → 1 行   (name → UPPER(name))
UDAF 多对一: N 行 → 1 行   (多行 → SUM/AVG)
UDTF 一对多: 1 行 → N 行   (一行 → explode 多行)

二、函数分类 & 注册

UDF 两种注册方式

scala 复制代码
import org.apache.spark.sql.functions._

// SQL 注册
spark.udf.register("myUpper", (s: String) => s.toUpperCase)
spark.sql("SELECT myUpper(name) FROM users")

// DSL 注册
val myUpperUdf = udf((s: String) => s.toUpperCase)
df.withColumn("upper_name", myUpperUdf(col("name")))

三、UDAF 深度对比 & Aggregator 生命周期

强类型 Aggregator(推荐)

scala 复制代码
import org.apache.spark.sql.expressions.Aggregator

case class Average(var sum: Double, var count: Long)

object AverageAggregator extends Aggregator[Double, Average, Double] {
  def zero: Average = Average(0.0, 0L)                    // 初始缓冲
  def reduce(b: Average, a: Double): Average = { b.sum += a; b.count += 1; b } // 分区内
  def merge(b1: Average, b2: Average): Average = { b1.sum += b2.sum; b1.count += b2.count; b1 } // 跨分区
  def finish(reduction: Average): Double = reduction.sum / reduction.count // 输出
  def bufferEncoder: Encoder[Average] = Encoders.product
  def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}

val avgUDAF = AverageAggregator.toColumn.name("avg_score")
ds.select(avgUDAF).show()

弱类型 UserDefinedAggregateFunction

scala 复制代码
class MyAvgUDAF extends UserDefinedAggregateFunction {
  def inputSchema = StructType(StructField("input", DoubleType) :: Nil)
  def bufferSchema = StructType(StructField("sum", DoubleType) :: StructField("count", LongType) :: Nil)
  def dataType = DoubleType
  def deterministic = true
  def initialize(buffer: MutableAggregationBuffer) = { buffer(0) = 0.0; buffer(1) = 0L }
  def update(buffer: MutableAggregationBuffer, input: Row) = { /* 累加 */ }
  def merge(b1: MutableAggregationBuffer, b2: Row) = { /* 合并 */ }
  def evaluate(buffer: Row) = buffer.getDouble(0) / buffer.getLong(1)
}
spark.udf.register("myAvg", new MyAvgUDAF)

四、性能陷阱与最佳实践

markdown 复制代码
⚠️ UDF 是黑盒 → Catalyst 无法优化
  · 无法谓词下推 · 无法 WholeStageCodegen · 逐行序列化调用

✅ 优化建议:
  1. 优先用 Spark SQL 内置函数
  2. 复杂逻辑用 Scala 表达式组合
  3. 必须用 UDF → Pandas UDF (Arrow 向量化, 快 100x)
  4. UDAF 优先用强类型 Aggregator

五、总结

  • 分类:UDF 一对一 / UDAF 多对一 / UDTF 一对多
  • 注册:SQL 用 register,DSL 用 functions.udf
  • UDAF:强类型 Aggregator 优于弱类型 UDAF

作者 :大数据技术实践者

博客 :blog.starzy.cn

GitHub :starzy1990.github.io

专注 AI Agent · LangGraph · RAG · 大数据架构 · 数据工程实践

相关推荐
数字新视界8 小时前
信创机房动环监控技术在智能运维中的应用与优势分析
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
清华kenny9 小时前
中医馆如何参考安徽亳州中医AI健康辨识仪品牌排名前十
大数据·人工智能·安徽晓得养
GlobalInfo10 小时前
全球及中国机器人现场部署服务行业研究报告(2026版):市场现状、行业前景与占有率分析
大数据·运维·人工智能
Elastic 中国社区官方博客11 小时前
5 分钟内将 56 页扫描文档转化为带引用的答案:面向搜索无法读取的文档的检索增强生成流水线
大数据·运维·人工智能·elasticsearch·搜索引擎·信息可视化·全文检索
STLearner11 小时前
KDD 2026 | (2月轮)时空数据(Spatial-Temporal)论文总结时空【下】(人群移动,城市科学(资源调度,)
大数据·人工智能·python·深度学习·机器学习·语言模型·数据挖掘
starzy199011 小时前
KafkaProducer 源码深度剖析:与 Broker 建立连接
大数据·kafka
2601_9659690613 小时前
AI求职能力证明指南:认证证书、项目作品与实际工作成果
大数据
EnCi Zheng13 小时前
AI Agent工具调用防护层怎么写-一份能直接跑的Python四层拦截实现
大数据·开发语言·python
byte轻骑兵14 小时前
【TextIn xParse + WorkBuddy】把8小时的标书审查压进1小时:招标要求逐条核验与废标风险预警工作台
大数据·数据库·aigc·textin·ai数据层基础设施
计算机毕业编程指导师14 小时前
2027计算机毕设选题指南:网站AI化、大数据预测+算法(XGBoost/LightGBM)、深度学习三方向57题+选题方法论
大数据·python·计算机·毕业设计·课程设计·毕设选题·1024程序员节