Spark UDF小结
前言
Spark UDF 增加了对 DS 数据结构的操作灵活性,但是使用不当会抵消Spark底层优化。
Spark UDF物理解释
文章1中提到 Spark UDF/UDAF/UDTF对数据的处理物理解释如下:
UDF =》一个输入一个输出。相当于map
UDAF =》多个输入一个输出。相当于reduce
UDTF =》一个输入多个输出。相当于flatMap
其中一个输入
这种概念不好理解,而Spark3.0.0官方文档2说明了是对数据行进行操作,与数据列无关:
Similar to Spark UDFs and UDAFs, Hive UDFs work on a single row as input and generate a single row as output, while Hive UDAFs operate on multiple rows and return a single aggregated row as a result. In addition, Hive also supports UDTFs (User Defined Tabular Functions) that act on one row as input and return multiple rows as output.
Spark UDF使用场景(排坑)
Spark UDF/UDAF/UDTF 可实现复杂的业务逻辑。但是,在Spark DS中,如列裁剪、谓词下推等底层自动优化无法穿透到UDF中,这就要求进入UDF内的数据尽可能有效。
以下的例子是由于误使用UDF导致的性能下降:
实现功能
筛选出搜索过特定词条的用户,并分析这些用户使用的app
数据schema
userDs的shema
DataFrame[appInputList: array<struct<inputList:array<struct<fwordSeg:array<string>,fwords:string,timestamp:bigint>>,packageName:string>>, citycode: int, date: int, useid: string]
代码实现(bad example)
filterRowQueryUdf 中匹配输入的query并裁剪出满足条件用户的app。本以为在UDF中做了裁剪,会减少数据量级。然后,忽略掉了输入的数据量较大,造成了性能瓶颈。
userDs.groupBy("userid").agg
Dataset<Row> userFilterDs = userDs.groupBy("userid")
.agg(collect_list(struct("date", "appInputList")).alias("date_appInputLists"))
.selectExpr("userid", "filterRowQueryUdf(date_appInputLists) as date_package")
.filter(col("date_package").isNotNull())
代码实现(优化后)
手动进行列裁剪,仅传入需要的字段到UDF。对于3TB的输入数据,计算耗时从30min降至7min.
Dataset<Row> userFilterDs = userDs
.selectExpr("userid", "explode(appInputList) as appInputList")
.selectExpr("userid", "explode(appInputList.inputList) as inputList")
.selectExpr("userid", "inputList.fwords as fwords")
.groupBy("userid")
.agg(collect_list("fwords").alias("fwords"))
//.filter("filterKeyWordUdf(fwords)")
.filter("filterQueryWordsOneUdf(fwords)")
.select("userid")
Dataset<Row> userAppDs = userDs
.selectExpr("userid", "date", "explode(appInputList) as appInputList")
.selectExpr("userid", "date", "appInputList.packageName as packageName")
.join(userFilterDs, "userid")
小结
了解Spark DS自动进行的优化,让处理逻辑顺应自动优化的方向,小码农也会有春天。
参考文献
1 SparkSql中UDF、UDAF、UDTF https://www.cnblogs.com/wuxiaolong4/p/11924172.html
- 揭秘新人机大战柯洁对手天壤 AI排名已力压Deepzen
- 一路走到java工程师,java都快出java9了,到底该如何学java?
- 网站发布合并bll问题的解决
- 痛并快乐着:浅谈大数据时代的分布式存储架构
- linux运维中的命令梳理(四)
- linux运维中的命令梳理(三)
- 轻松水印-批量提取exif信息加水印的工具
- Enterprise Library 4.1学习笔记7----缓存应用程序块之SqlDependency
- linux运维中的命令梳理(一)
- 可视化你的BLAST结果
- linux运维中的命令梳理(二)
- VB-取日期属于星期几
- 全球AI新闻创新实践系列③:华邮、雅虎、美联社、Quartz怎么干!
- nginx+php负载均衡集群环境中的session共享方案梳理
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android TextView 去掉自适应默认的fontpadding的实现方法
- Linux文件/目录的权限及归属管理使用
- Android自定义环形LoadingView效果
- Android隐藏标题栏及解决启动闪过标题的实例详解
- Linux使用sed命令替换字符串教程
- Android实现获取短信验证码并自动填写功能
- Android 定时器实现图片的变换
- Android 软键盘状态并隐藏输入法的实例
- Linux磁盘管理之LVM的使用
- Android编程之菜单Menu的创建方法示例
- Ubuntu下Docker CE的安装
- 基于Android自定义控件实现雷达效果
- Android 中 onSaveInstanceState()使用方法详解
- Linux修改主机名的简单方法
- Android RecycleView使用(CheckBox全选、反选、单选)