【hive】transform脚本

文档地址:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Transform

一、介绍

和udf差不多的作用,支持用python实现。通过标准输入流从hive读取数据,内部处理完再通过标准输出流将处理结果返回给hive。实现流程上比udf要更简单灵活一些,只需要上传脚本=>add file加载到分布式缓存=>使用。

二、实现

先定义一个名为transform.py的脚本,将传入的两个字段值都+1。

#!/usr/bin/env python
import sys

for line in sys.stdin:
    try:
        x, y = map(float, line.strip().split('\t'))
        x += 1
        y += 1
        print('\t'.join(map(str, [x, y])))
    except ValueError as e:
        print('\t'.join([r'\N'] * 2))

上面对输入流按照\t分隔是因为hive中的数据在传递到py脚本时,多个字段间默认会用\t分隔拼接为字符串,并且空值null会被转为字符串\N。同样将处理结果返回给hive时,如果多个字段,为了hive能够正确解析,也需要用\t拼接输出,单独的\N在hive中也会被重新解释为null。
在这里插入图片描述
除了单独的\N会被重新解释为null外,还有一种情况也会被hive解释为null,就是脚本里返回的字段个数小于hive中接收的字段个数时,hive中多余的字段会被赋值为null。

1.脚本上传到本地

这里的本地指的是hive主服务hive server2所在的节点,也就是我们客户端连接的那个机器。

先上传到主服务机器下的某个路径:

# 文件上传路径
[root@node1 HiveLib]# readlink -e transform.py
/root/HiveLib/transform.py

上传后通过add file命令将脚本添加到分布式缓存,之后就可以直接使用了。

-- 添加到分布式缓存
add file /root/HiveLib/transform.py;

-- 创建一个临时表测试执行
with `table` as (
    select '1' as id, '1.6789' as col1, '7.13' as col2
    union all
    select '2' as id, '11.568' as col1, null as col2
    union all
    select '3' as id, '26.09761' as col1, '71.89002' as col2
)
-- as后面接收脚本返回值的字段也可以指定字段类型, eg:(col1 double, col2 double), 省略时默认都是字符串string类型
select transform (col1, col2) using 'transform.py' as (col1, col2) from `table`;

在这里插入图片描述

2.脚本上传到hdfs

这种方式和本地实现基本一致,只不过需要将脚本上传到hdfs中,add file时后面跟的是hdfs路径。

[root@node1 HiveLib]# hadoop fs -put ./transform.py /user/hive/lib
[root@node1 HiveLib]# hadoop fs -ls /user/hive/lib
Found 2 items
-rw-r--r--   3 root supergroup       4164 2022-12-18 00:48 /user/hive/lib/hive_udf-1.0-SNAPSHOT.jar
-rw-r--r--   3 root supergroup        257 2024-05-05 19:13 /user/hive/lib/transform.py

sql客户端中执行:

-- 脚本路径换为hdfs路径
add file hdfs://node1:8020/user/hive/lib/transform.py;

with `table` as (
    select '1' as id, '1.6789' as col1, '7.13' as col2
    union all
    select '2' as id, '11.568' as col1, null as col2
    union all
    select '3' as id, '26.09761' as col1, '71.89002' as col2
)
select transform (col1, col2) using 'transform.py' as (col1, col2) from `table`;

在这里插入图片描述

三、几个注意点

1.脚本名不要写全路径

using语句后面指定脚本只写脚本名即可,不要写全路径。全路径的话会报错[08S01][20000] Error while processing statement: FAILED: Execution Error, return code 20000 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask. Unable to initialize custom script.,参考
https://stackoverflow.com/questions/15106127/i-met-an-error-when-i-using-hive-transform-features,我也不太理解为什么有这要求,先照做就行。
在这里插入图片描述

2.using后面语句中,带不带"python"的问题

这里说的是sql语句中,是using 'transform.py'还是using 'python transform.py'的问题。可以不带python这个关键字,但是前提脚本中必须指定了Shebang,类似于#!/usr/bin/env python这样,指定脚本的解释器。如果指定Shebang,using后面带不带python都可以,如果脚本中没指定,using后面必须带python这个关键字,否则报错。

看到有人说需要给py脚本chmod +x transform.py赋予可执行权限,实际操作中经过验证本地和hdfs都不需要。

3.py脚本Shebang:#!/usr/bin/env python

Shebang(也称为Hashbang)是一个源于Unix系统中的概念,特别是在类Unix操作系统中广泛使用。它是指脚本文件第一行以#!开头的特殊注释行,用于指定该脚本应该由哪个解释器程序来执行。这个名称来源于这两个起始字符—井号(#)和叹号(!)。

主要解释下#!/usr/bin/env python#!/usr/bin/python的区别。两者都是用来指定该脚本的解释器,但是前者比后者有更好的兼容性,可以理解为:后者是指定了一个固定的解释器路径,虽然多数情况下遵循规范解释器路径会在该目录下,但是并不能保证一定存在。而前者逻辑上等价于env | grep python,它是从当前所有的环境变量中按照一定的优先级顺序去找python解释器,最先找到哪个就用哪个执行,所以可以有效避免路径指定错误的问题,推荐前面这种写法。

[root@node1 HiveLib]# which python
/root/anaconda3/bin/python
[root@node1 HiveLib]# which env
/usr/bin/env
[root@node1 HiveLib]# env | grep python
CONDA_PYTHON_EXE=/root/anaconda3/bin/python

4.通过约定增强脚本的通用性

hive transform脚本相比udf有个缺点,就是select中如果使用了transform就不能再选择其它字段了,而实际业务需求每次处理的字段并不总是固定。为了避免每次需求变化而导致需要修改脚本,可以在调用时通过通配符*将外部所有参数传递给脚本,脚本处理完再将所有参数返回。这样即使字段发生变化,也只需要在外部sql中动态调整接收脚本返回值的列名即可。这个过程中需要处理的字段在输入输出时只需要按照约定放在一个指定的位置就行了。
select transform (*) using 'transform.py' as (col1, col2, ...) from `table`;

5.指定python执行包和第三方库路径

将python环境打tar包,上传hdfs:

map@gzdt-map-poi-yingxiang-offline04 venv$ gfs -ls /hdfs_path/pyenv
24/06/05 16:08:12 INFO fs.LibdfsLoader: Trying to load the libdfs library...
24/06/05 16:08:12 INFO fs.LibdfsLoader: Loaded the libdfs library
24/06/05 16:08:12 INFO fs.DFileSystem: Loaded the libdfs library
Found 2 items
-rwxrwxrwx   3 poi-yingxiang poi-yingxiang   87745890 2024-05-11 18:33 /hdfs_path/pyenv/python3.tar.gz
-rwxrwxrwx   3 poi-yingxiang poi-yingxiang   90376769 2024-06-05 16:08 /hdfs_path/pyenv/python3_shapely.tar.gz

transform脚本中指定使用python3.tar.gz这个包及包内的第三方库作为环境执行:

set `spark.sql.she.replaceAddResources`=true;		# 允许同名资源覆盖替换
add file hdfs://hdfs_path/hive_udf/xy_aoi_fix.py;
add archive hdfs://hdfs_path/pyenv/python3_shapely.tar.gz;

select
    transform(*) using 'PYTHONPATH=python3.tar.gz/lib/python3.9/site-packages python3.tar.gz/bin/python3 xy_aoi_fix.py'
    as(col1, col2, ...)
from `table`;

6.python执行包tar包安装第三方库

脚本中用到了shapely这个库,sql执行失败,查看spark(hive on spark环境)日志提示该模块未安装。

解压安装包,进入bin目录下,执行python -m pip install package_name,默认会安装到lib/python/site-packages目录下,安装完重新打包就行。

解压缩相关命令:

tar -zcf xxx.tar.gz ./* (压缩当前文件夹下所有)
tar -zxf xxx.tar.gz -C ./ (解压到当前文件夹)
tar -tf xxx.tar.gz (不解压 只查看压缩包内容)

四、hive<=>py数据交互时换行符的处理

hive在和py脚本数据交互时,都是通过\n标记一行数据的结束。数据从hive传递到py时,hive会在最后一个传递的字段后添加\n,在从py接收返回数据时,hive也是以\n来划分数据行。

下面创建一个test_transform.py脚本,传入一列bid,接收两列返回值bid、bid+1,验证hive对换行符的处理。

select 
    transform(bid) using 'PYTHONPATH=python3.tar.gz/lib/python3.9/site-packages python3.tar.gz/bin/python3 test_transform.py'
    as(bid,bid_trans)
from 0712_step2;

demo1(❌)

import sys

for line in sys.stdin:
    cols = line.split('\t')
    try:
        cols.append(str(int(cols[0]) + 1))
        print('\t'.join(cols))
    except ValueError:
        continue

在这里插入图片描述
数据传递到py时,bid字段后会默认添加换行符,当内部处理完成后,print默认也会在数据末尾添加一个换行符。

demo2(❌)

import sys

for line in sys.stdin:
    cols = line.split('\t')
    try:
        cols.append(str(int(cols[0]) + 1))
        print('\t'.join(cols), end='')
    except ValueError:
        continue

在这里插入图片描述
传入字段的换行符不处理,传出时数据末尾不添加换行符。

demo3(✅)

import sys

for line in sys.stdin:
    cols = line.strip('\n').split('\t')
    try:
        cols.append(str(int(cols[0]) + 1))
        print('\t'.join(cols))
    except ValueError:
        continue

在这里插入图片描述

五、设置多个PYTHONPATH路径

起因部署一个线上任务时报错,重新弄了一套和开发机一样的版本环境部署上去spark又报错:
在这里插入图片描述
查看encodings模块所在路径:

map@gzdt-map-poi-yingxiang-offline04 bin$ ./python
Python 3.6.5 (default, Aug 12 2024, 11:34:41)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import encodings
>>> encodings.__file__
'/home/disk1/wangdeyong/venv/python365/lib/python3.6/encodings/__init__.py'
>>> exit()

该模块为python内置模块,和site-packages处于同一层级,所以hivesql中只指定site-packages路径就会找不到encodings模块,解决办法通过冒号:在sql中指定多个path路径:

select
    transform(*) 
    using 'PYTHONPATH=python36.tar.gz/lib/python3.6/site-packages:python36.tar.gz/lib/python3.6:python36.tar.gz/lib/python3.6/lib-dynload 
    python36.tar.gz/bin/python3 aoi_center_adjust.py'
    as (bid, src_type, sub_src, update_time, problem_id, field_name1, center_x, point_x, field_name2, center_y, point_y)
from base;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值