python udf存在的问题:
在数据清洗过程中,如果使用的是TransForm而不是UDF的话,因为Python是直接向系统申请资源的,而不是像ResourceManager申请资源,故会导致启动的Python脚本对内存和CPU的使用不可控,尤其是当启动多个Map时,因为一个map将启动一个Python因此,当同时运行的map有几十个时(测试集群较小),同时将尝试启动相同个数的python(资源够用的话仍然会启动几十个),且此时Map占用的内存是不会释放掉的他在一直等待Python的结果,这将导致python可用的资源仅仅是原本分配给系统的很少的资源(注:在安装Hadoop时,对于单个节点,一般仅仅给系统留出很少的内存,其他的内存全部分给了集群。例如32G物理内存的节点给系统和dataNode+nodeManager的内存就4-8个G,同时CPU核数也不足节点的一半,剩余的内存和cpu核数全部划分给集群使用。需要注意的是,这里虽然说是划分给集群使用,仅仅是逻辑上的划分,即规定集群可以使用的最大的物理内存,超过该内存时MR可以认为是不会抢占分配给系统+DataNode+nodeManager的内存的,但是当集群中没有MR在执行,即没有map或者reduce在执行时,划分给集群的这部分资源是可以被系统使用的。而若有map和Reduce在执行时,运行map和reduce的JVM的资源不会因为系统进程需要使用而被释放掉)所以,所有正在执行的Map一直在等待python的运行结果而没有释放掉其自身占用的资源,故python无法使用分配给集群的资源而只能使用预留给系统+nodeManager+DataNode的4-8G的内存和很少的cpu核数。因此会导致集群的资源无法被高效利用。
综上,使用Transform(Python)执行效率低的根本原因在于Python是直接向操作系统申请资源,而不是向YARN的ResourceManager申请资源,故而导致节点的资源无法高效组织和被利用。此外,不要轻易使用transform!不要轻易使用transform!不要轻易使用transform
简介
Hive为我们提供了众多的内置函数,但是在实际的运用过程中仍然不能满足我们所有的需求.hive是用java开发的,本身提供了使用java去开发UDF的方式.而这里我们采用python的方式去实现UDF函数.
DEMO实现
我们这里用python自定义函数,去实现一个方法,利用身份证号去判断性别(18位身份证的倒数第二位偶数为女,奇数为男.15位身份证的倒数第一位偶数为女,奇数为男.).其实这个需求可以使用hive自带的function去进行解决.我们接下来使用2种方式去实现这个需求.
数据准备
我们在hive上创建一个external表(名字person表),执行如下代码:
create external table person(
name string,
idcard string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED as TEXTFILE;
该表两个字段,一个为name,另一个为idcard
数据格式如下:
neil 411326199402110030
pony 41132519950911004x
jcak 12312423454556561
tony 412345671234908
field分隔符使用\t
我们将数据放入hive的warehouse中:
hdfs dfs -put person.txt /user/hive/warehouse/test_neil.db/person
执行select,我们发现数据已经进入到hive了.
使用Hive Function去实现
我们可以执行一下的hql去实现
select idcard,
case when length(idcard) = 18 then
case when substring(idcard,-2,1) % 2 = 1 then '男'
when substring(idcard,-2,1) % 2 = 0 then '女'
else 'unknown' end
when length(idcard) = 15 then
case when substring(idcard,-1,1) % 2 = 1 then '男'
when substring(idcard,-1,1) % 2 = 0 then '女'
else 'unknown' end
else '不合法' end
from person;
得到的结果如下(beeline下):
+---------------------+------+--+
| idcard | _c1 |
+---------------------+------+--+
| 12312423454556561 | 不合法 |
| 123124234545565 | 男 |
| 411325199308110030 | 男 |
| 41132519950911004x | 女 |
UDF编写
如下是我们的udf代码:
# -*- coding: utf-8 -*-
import sys
for line in sys.stdin:
detail = line.strip().split("\t")
if len(detail) != 2:
continue
else:
name = detail[0]
idcard = detail[1]
if len(idcard) == 15:
if int(idcard[-1]) % 2 == 0:
print("\t".join([name,idcard,"女"]))
else:
print("\t".join([name,idcard,"男"]))
elif len(idcard) == 18:
if int(idcard[-2]) % 2 == 0:
print("\t".join([name,idcard,"女"]))
else:
print("\t".join([name,idcard,"男"]))
else:
print("\t".join([name,idcard,"身份信息不合法!"]))
这里我们使用python的重定向,将hive控制台的输出进行split,split默认使用的为\t.然后根据split后的idcard的倒数第二位进行判断这个人的性别.
测试
我们在hive中去执行查询时,报错的提示不是很详细.我们可以使用cat指令去测试python脚本的执行效果.
我们在终端中执行如下指令:
cat person.txt|python person.py
输入结果如下:
neil 411325199308110030 男
pony 41132519950911004x 女
jack 12312423454556561 身份信息不合法!
tony 123124234545565 男
说明我们的解析是成功的.
使用
我们在hive中使用python定义的UDF函数要借助transform函数去执行.
transform函数的语法如下:
SELECT TRANSFORM (<columns>)
USING 'python <python_script>'
AS (<columns>)
FROM <table>;
transfrom和as的columns的个数不必一致.
我们首先需要将我们的person.py加载入
我们在hive中去执行如下代码:
add file /xxx/person.py
xxx为本地文件的路径.
然后使用transform函数执行:
select transform(name,idcard) USING 'python person.py' AS (name,idcard,gender) from person;
我们同样可以得到如下的结果:
neil 411325199308110030 男
pony 41132519950911004x 女
jack 12312423454556561 身份信息不合法!
tony 123124234545565 男