hive:创建自定义python UDF

由于Hadoop框架是用Java编写的,大多数Hadoop开发人员自然更喜欢用Java编写UDF。然而,Apache也使非Java开发人员能够轻松地使用Hadoop,这是通过使用Hadoop Streaming接口完成的!

Java-UDF vs. Python-UDF

Java 实现 UDF,需要引用包含 Hive API 的外部 jar 包,而 Python 无需引起其他外部包;
Java 实现 UDF 后,需要打包后才可被 HiveQL 调用,而通过 Python 实现 UDF 后,可以在 HiveQL 中直接被调用;
Java 实现 UDF,对读入和输出数据方式没有要求,实现的 UDF 可以输入一条记录的指定列数据,输出结果可以直接在 HiveQL 的 WHERE 中用于判断条件使用;Python 实现的 UDF,对读入和输出数据方式有特殊要求,需要对 HiveQL 中表的指定列数据批量读入,然后一对一地批量输出,因此,通过 Python 实现的 UDF 可以结合子查询使用。

Python-UDF开发流程

1 编写Python数据处理脚本

employees.py:

# -*- coding: utf-8 -*-

import sys

for line in sys.stdin:
   line = line.strip()
   (emp_id,emp_name) = line.split('\t')
   print(emp_id + '\t' + emp_name + ',亲')

Note: py代码最好都加上# -*- coding: utf-8 -*-

通过 Python 实现 Hive 的 UDF,Python 脚本需要以特定的方式读入和输出,除了必须引用 sys 包外,无须引用其他外部包。Python 实现的 UDF,需要批量的读入数据,并一对一的批量输出。

打印print信息

py udf中stdout是数据流,不是输出流。要输出信息,需要将log打印到stderr中。
def log(msg):
    t = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    sys.stderr.write('{}  {}\n'.format(t, msg))
    sys.stderr.flush() 

2 hive中使用

需要将自定义Python数据处理文件添加到路径中,然后在查询中应用transform函数。
add file /path/employees.py;
select transform(emp_id, emp_name) using 'python employees.py' as (emp_id, emp_name) 
from employees;

其中, SELECT 中的 columns 是 FROM 中 table 的列名, 而 AS 中的 columns 是经过 USING 中 Python 脚本 python_script 计算返回的列名。

使用transform的时候不能查询别的列;所以要把主键id也传进去,然后和真实要处理的列一起返回。再通过主键id去join原表。

[使用Python编写Hive UDF]

Note:

1 使用默认的py可能只需要:
ADD FILE /path-to-my-script/my_python_code.py;
USING '/path-to-my-script/my_python_code.py'

也可能是py代码里面指定了#!/usr/local/bin/python。

[How to create a custom UDF for Hive using Python - Cloudera Community - 248486]

2 如果未配置py可能出错:Log Type: syslog_attempt_***
Caused by: java.io.IOException: Cannot run program "py_env.zip/bin/python": error=2, No such file or directory
解决:ADD ARCHIVE hdfs://.../snapshot/py_env.zip;

自定义py环境构建

也可以自己构建py环境:
ADD ARCHIVE hdfs://path/python.zip;
然后通过using 'python.zip/bin/python employees.py'来使用指定py环境。

构建python包的方式可能有:
1 如果平台支持,直接写requirement文件自动安装并构建包
2 本地打包成zip再上传。Note: 打包也许可以参考一下[打包运行环境conda-pack]

建议重新配置一个py环境,看哪些包是必须的,减小上传py环境大小。

py udf读取目录

方式1(推荐):

先上传文件或者git地址到hdfs,都放在HDFS路径/*下。
直接在hive中读入目录,注意目录在命令中的写法形式

ADD FILE HDFS路径/.../models;
select
    TRANSFORM (id, text) using 'py_env.zip/bin/python inference.py models/submodel/onnx' as id, label, score
Note:
1 使用ADD FILEs和ADD FILE貌似没啥区别,ADD FILE也可以加整个目录。
2 当然也可以ADD FILE hdfs://.../onnx;...inference.py onnx'...。不过必须保证add file的最后一级目录要写到using字符串命令里面,否则读取不到数据。
3 transformers模型读取时如果读取不到目录里面的文件,就可以报错:AutoConfig.from_pretrained
There was a problem when trying to write in your cache folder (/home/.cache/huggingface/hub). You should set the environment variable TRANSFORMERS_CACHE to a writable directory.
PermissionError: [Errno 13] Permission denied: '/home/.cache'[Accessible location for cache folder]或者Caused by: org.apache.hadoop.ipc.RemoteException(java.io.FileNotFoundException): Path is not a file

4 pyudf中目录查看
log("str(os.getcwd()):\n" + str(os.getcwd()))
log("model_path:\n" + model_path)
for root, ds, fs in os.walk(os.getcwd()):
    for f in fs:
        log(os.path.join(root, f))
str(os.getcwd()):
/***/hadoop/yarn/***/appcache/application_***/container_***
model_path:
models/submodel/onnx
walk整个os.getcwd()目录的结果:
/***/hadoop/yarn/***/appcache/application_***/container_***/launch_container.sh
/***/hadoop/yarn/***/appcache/application_***/container_***/container_tokens
/***/hadoop/yarn/***/appcache/application_***/container_***/tez-conf.pb
/***/hadoop/yarn/***/appcache/application_***/container_***/hive-exec-***.jar
/***/hadoop/yarn/***/appcache/application_***/container_***/inference.py
/***/hadoop/yarn/***/appcache/application_***/container_***/user-resource/warehouse-udf-dist.jar
Note: walk整个目录没看到model_path,可能是在jar包或者conf指定的某个目录下吧。

方式2:
将目录压缩成zip文件,在hive中读入zip文件,再在代码里面解压
import zipfile
model_path = sys.argv[1]
zip_obj = zipfile.ZipFile(model_path, 'r')
zip_obj.extractall('model_name')
zip_obj.close()
model_path = 'model_name'

[zipfile --- 使用ZIP存档 — Python 3.11.4 文档]

方式3:也可以打包成zip文件后直接通过目录调用
如将py文件(或所在文件夹dir/subdir/)直接打包成zip,然后应该是自动解压的,直接通过目录调用py文件就可以。
ADD ARCHIVE hdfs://***snapshot.zip;
TRANSFORM(***) USING '***/python snapshot.zip/dir/subdir/***.py'

3 带文件带输入参数lr二分类示例

ADD ARCHIVE hdfs://path/python3.zip;
ADD FILE hdfs://path/lr_binary_parameter.json;
ADD FILE hdfs://path/lr_predict.py;
SELECT
TRANSFORM(id,f1,f2,f3)
USING 'python3.zip/python36/bin/python lr_predict.py lr_binary_parameter.json binary'
AS (id BIGINT,lr_score DOUBLE);

[Scikit-learn:分类classification_-柚子皮-的博客-CSDN博客]

hive python udf bugs

Caused by: java.io.IOException: Broken pipe
这个错误其实可能是任何py代码中的错误。主要去看log里面的Log Type: stderr。

from:hive:创建自定义python UDF_python实现hive自定义函数_-柚子皮-的博客-CSDN博客

ref:

猜你喜欢

转载自blog.csdn.net/pipisorry/article/details/130937663