写在前面:
工作中常用的一个数据场景是这样的: 有一个很小的文件 code_file.csv ,里面是一些编码及含义(如:1001,北京),
还有一个待转化处理的数据集存在HIVE表中,其中一个字段code就是要利用文件code_file.csv转成对应中文含义的字段。
当我们使用Spark来处理这样一个大数据集和一个小文件的时候,当然你可以把小文件导入到HIVE表中,使用JOIN函数进行转换。
但JOIN的成本是很高的,好的方法当然是将小文件缓存起来,使用类似将小文件广播到每个执行节点上,写一个UDF来解决。
这里我用的是python版的spark,所以对于pyspark,你不得不知道的事情是:
(1)pyspark添加外部依赖普通文件的方法?
- 一个文件和多个文件的添加方法有哪些,有什么区别?
- 在程序里调用添加的文件时,是否需要指定的API接口或函数来获取?
- 或者添加的文件被加载到了什么地方,excutor执行节点如何获取这些文件?
(2)pyspark添加外部依赖py文件的方法?
- 一个py文件和多个py文件的添加方法有哪些,有什么区别?
- 在主程序中调用添加的py文件的方法是什么?有什么需要注意的?
- 或者添加的py文件相互调用其中的方法或模块有什么需要额外注意的吗?
对于上述问题的回答,关键就在于spark-submit时的参数选项。--files 和 --py-files (不熟悉spark-submit可选参数的同学,可参考我之前的博客:spark的生产应用提交脚本spark-submit)
--py-files PY_FILES | Comma-separated list of .zip, .egg, or .py files to place on the PYTHONPATH for Python apps. | 逗号分隔的.zip , .egg, .py文件列表 |
--files FILES | Comma-separated list of files to be placed in the working directory of each executor. File paths of these files in executors can be accessed via SparkFiles.get(fileName). | 逗号分隔的文件列表,会被加载存放到工作节点路径下,可直接按照文件名或路径来读取文件 |
就是说,只要我们在提交spark任务的时候把外部依赖文件使用--files 选项来配置,那么我们就可以在主程序中直接读取这个文件了。 一点都不复杂。
提交spark任务 spark-submit.sh
#!/bin/sh
spark-submit \
--master yarn \
--queue {所在集群的队列} \
--deploy-mode client \
--driver-memory 4G \
--driver-cores 4 \
--executor-memory 8G \
--executor-cores 4 \
--num-executors 100 \
--conf spark.default.parallelism=200 \
--files /home/{存放文件的路径}/code_file.csv \
--name "udf" \
udf.py
推荐其他博客阅读:通过pyspark提交多个执行文件和文本文档
如果你已经读完我上一篇博客 注册spark UDF实例1【入门必修第一篇,简单函数注册】,那这里关于UDF就无需多言了,我们直接上代码吧:
启动一个spark进程,读取文件,写一个处理函数,将函数注册成UDF函数,最后进行调用,完成转换处理操作
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time: 2021/2/9 5:37 下午
# @Author: Toby Gao
# @File: udf_test
import os, sys, datetime, time
from sys import argv
# encoding=utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
if __name__ == '__main__':
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("SparkUdfTest") \
.enableHiveSupport() \
.config("spark.debug.maxToStringFields", "300") \
.getOrCreate()
#读入进来CODE文件
csv_file = "code_file.csv"
cate_code_dict = {}
for line in open(csv_file,"r"):
line = line.strip()
arr = line.split(',')
if len(arr) < 2:
continue
else:
code = arr[0]
name = arr[1]
if code not in cate_code_dict.keys():
cate_code_dict[code] = name
else:
continue
#第一步,写一个处理函数
def pad_cate_codes(k):
if k in cate_code_dict.keys():
return cate_code_dict[k]
else:
return "unknow"
#第二步,将处理函数注册成spark-Sql的函数
spark.udf.register("padudf",pad_cate_codes)
#第三步,在spark.sql中调用UDF函数
df = spark.sql("""
select padudf(cate_code) from table""")
df.show()
最后,使用 spark-submit.sh 脚本提交spark任务,查看Spark执行结果就完成了。