文档地址:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Transform
- 一、介绍
- 二、实现
- 三、几个需要注意的点
-
- 1.脚本名不要写全路径
- 2.using后面语句中,带不带"python"的问题
- [3.py脚本Shebang:#!/usr/bin/env python](#!/usr/bin/env python)
一、介绍
和udf差不多的作用,支持用python实现。通过标准输入流从hive读取数据,内部处理完再通过标准输出流将处理结果返回给hive。实现流程上比udf要更简单灵活一些,只需要上传脚本=>add file加载到分布式缓存=>使用。
二、实现
先定义一个名为transform.py
的脚本,将传入的两个字段值都+1。
py
#!/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所在的节点,也就是我们客户端连接的那个机器。
先上传到主服务机器下的某个路径:
sh
# 文件上传路径
[root@node1 HiveLib]# readlink -e transform.py
/root/HiveLib/transform.py
上传后通过add file命令将脚本添加到分布式缓存,之后就可以直接使用了。
sql
-- 添加到分布式缓存
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路径。
sh
[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客户端中执行:
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解释器,最先找到哪个就用哪个执行,所以可以有效避免路径指定错误的问题,推荐前面这种写法。
sh
[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