写在前面:

工作中常用的一个数据场景是这样的: 有一个很小的文件 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执行结果就完成了。