本文为您介绍使用Python语言编写的MaxCompute UDF的常见问题。
类或资源问题
调用MaxCompute UDF运行代码时的常见类或资源问题如下:
问题现象一:运行报错描述为
function 'xxx' cannot be resolved
。产生原因:
原因一:调用MaxCompute UDF运行代码时,所处的项目不正确。即MaxCompute UDF不在MaxCompute项目中。例如MaxCompute UDF注册到了开发项目,但却在生产项目执行调用操作。
原因二:MaxCompute UDF的类不正确或资源不正确。
原因三:MaxCompute UDF依赖的资源类型不正确。例如PY文件,资源类型是PY,但MaxCompute UDF代码中
get_cache_file
需要的类型是FILE。原因四:MaxCompute UDF依赖的资源不是最新的。当您通过DataWorks上传MaxCompute资源时,从DataWorks同步至MaxCompute会存在延时情况,非最新资源。
原因五:Python环境版本不正确。MaxCompute默认采用Python 2运行作业,当Python代码中存在非ASCII编码字符时,运行会报错。
解决措施:
原因一的解决措施:在报错的项目下通过MaxCompute客户端执行
list functions;
命令,确保MaxCompute UDF是真实存在的。原因二的解决措施:通过MaxCompute客户端执行
desc function <function_name>;
命令,检查输出结果中的Class及Resources的正确性。如果不正确,需要执行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注册函数,其中package_to_class为Python脚本名.类名,resource_list为MaxCompute中需要引用的所有文件资源、表资源、压缩包资源或第三方包。更多注册函数操作,请参见注册函数。
原因三的解决措施:通过MaxCompute客户端执行
desc resource <resource_name>;
命令,检查输出结果中的Type的正确性。如果资源类型不正确,可执行add <file_type> <file_name>;
重新添加资源。如果MaxCompute UDF代码中的引用资源方式为
get_cache_file
,表明引用的是文件资源,资源类型必须为FILE。如果MaxCompute UDF代码中的引用资源方式为
get_cache_table
,表明引用的是表资源,资源类型必须为TABLE。如果MaxCompute UDF代码中的引用资源方式为
get_cache_archive
,表明引用的是压缩包资源,资源类型必须为ARCHIVE。
更多上传资源操作,请参见添加资源。
原因四的解决措施:通过MaxCompute客户端执行
desc resource <resource_name>;
命令,检查输出结果中的LastModifiedTime,确保为最近一次变更的时间。原因五的解决措施:在Python代码头部增加
#coding:utf-8
或# -*- coding: utf-8 -*-
编码声明,或在调用MaxCompute UDF的SQL语句前增加set odps.sql.python.version=cp37;
与SQL语句一起提交,在Python 3环境下运行作业。
问题现象二:MaxCompute UDF中使用
get_cache_archive('xxx.zip')
时,运行报错描述为IOError: Download resource: xxx.zip failed
、odps.distcache.DistributedCacheError
或fuxi job failed: Download resource failed: xxx.zip
。产生原因:
原因一:压缩包资源不存在。注册MaxCompute UDF时未同步指定压缩包资源。
原因二:压缩包资源类型不正确,非ARCHIVE。
原因三:压缩包资源名及后缀格式与实际资源包名或后缀格式不一致。例如压缩包资源名及后缀为xxx.zip,但实际上传的文件是xxx.tar.gz,此时会按ZIP格式解压,导致解压失败报错。
原因四:同一个作业中有两个UDF依赖了不同项目下的同名资源。
解决措施:
原因一的解决措施:通过MaxCompute客户端执行
desc function <function_name>;
命令,检查输出结果中的Resources是否包含报错信息中的压缩资源包。如果不存在,可执行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注册函数,并在resource_list中加上缺失的压缩包资源。更多注册函数操作,请参见注册函数。
原因二的解决措施:通过MaxCompute客户端执行
desc resource <resource_name>;
命令,检查输出结果中的Type是否为ARCHIVE。如果不是ARCHIVE类型,可执行
add archive <file_name>;
命令重新上传资源。更多上传资源操作,请参见添加资源。
原因三的解决措施:通过MaxCompute客户端执行
desc function <function_name>;
命令,检查输出结果中的Resources中的压缩包资源名及后缀是否与实际文件名、后缀一致。如果不一致,可执行
add archive <file_name>;
命令重新上传资源,file_name必须与实际压缩包资源名及后缀保持一致。原因四的解决措施:排查作业依赖的所有UDF(包括视图中依赖的UDF),检查UDF所属项目和对应资源的名称。如果不同项目下存在同名资源,建议修改依赖的UDF或资源名称。
问题现象三:MaxCompute UDF中使用
get_cache_table(table_name)
时,运行报错描述为odps.distcache.DistributedCacheError: Table resource "xxx_table_name" not found
。产生原因:
原因一:表资源不存在。注册MaxCompute UDF时未同步指定表资源。
原因二:表资源类型不正确,非TABLE。
解决措施:
原因一的解决措施:通过MaxCompute客户端执行
desc function <function_name>;
命令,检查输出结果中的Resources是否包含报错信息中的表资源。如果不存在,可执行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注册函数,并在resource_list中加上缺失的表资源。更多注册函数操作,请参见注册函数。
原因二的解决措施:通过MaxCompute客户端执行
desc resource <resource_name>;
命令,检查输出结果中的Type是否为TABLE。如果不是TABLE类型,可执行
add table <table_name>;
命令重新上传表资源。更多上传资源操作,请参见添加资源。
问题现象四:MaxCompute UDF引用第三方包时,运行报错描述为
ImportError: No module named 'xxx'
。产生原因:
原因一:第三方包的资源类型不正确,非ARCHIVE。
原因二:注册MaxCompute UDF时,未同步指定第三方包。
原因三:MaxCompute UDF代码中未添加第三方包路径。
原因四:第三方包为WHEEL包,但后缀不正确。您需要根据Python环境版本下载对应的WHEEL文件。
原因五:第三方包不是WHEEL包,且非纯Python包,但包中存在setup.py文件。
原因六:MaxCompute UDF对应的PY文件名称与需要引用的第三方模块的名称冲突。例如MaxCompute UDF对应的Python文件是A.py,import A时默认会导入A.py而不是三方包里的模块。
解决措施:
原因一的解决措施:通过MaxCompute客户端执行
desc resource <resource_name>;
命令,检查输出结果中的Type是否为ARCHIVE。如果不是ARCHIVE类型,可执行
add archive <file_name>;
命令重新上传资源。更多上传资源操作,请参见添加资源。
原因二的解决措施:通过MaxCompute客户端执行
desc function <function_name>;
命令,检查输出结果中的Resources是否包含第三方包。如果不包含,可执行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注册函数,并在resource_list中加上第三方包。更多注册函数操作,请参见注册函数。
原因三的解决措施:检查MaxCompute UDF代码中是否添加了第三方包路径,即是否配置了
sys.path.insert(0, 'work/第三方包路径')
。假设模块名称为A,对应Python文件为A.py,确定其资源包路径及在代码中添加路径的方法如下:假设PY文件位于文件夹resource_dir中,如果将文件夹resource_dir直接压缩为resource-of-A.zip,
sys.path.insert
中填写的路径为work/resource-of-A.zip/resource_dir/
。假设PY文件位于文件夹resource_dir中,如果将文件夹resource_dir内的所有文件压缩为resource-of-A.zip,
sys.path.insert
中填写的路径为work/resource-of-A.zip/
。假设PY文件位于文件夹resource_dir/path1/path2中,如果将文件夹resource_dir内的所有文件压缩为resource-of-A.zip,
sys.path.insert
中填写的路径为work/resource-of-A.zip/path1/path2/
。
说明ARCHIVE资源默认放在MaxCompute UDF执行路径的相对路径
./work/
中。原因四的解决措施:Python 2和Python 3环境对应的WHEEL文件不相同,Python 2要求WHEEL文件名称中包含
cp27-cp27m-manylinux1_x86_64
,Python 3要求WHEEL文件名称中包含cp37-cp37m-manylinux1_x86_64
,请下载合适的WHEEL文件。下载的WHEEL文件可以直接修改后缀为.zip,不需要对WHEEL文件再次打包生成ZIP文件。原因五的解决措施:需要先在与MaxCompute兼容的环境下将setup.py编译生成WHEEL包,然后再执行上传资源及注册函数操作。更多编译第三方包信息,请参见使用需要编译的第三方包。
原因六的解决措施:修改MaxCompute UDF对应的Python文件名称。
问题现象五:MaxCompute UDF引用Python 3的标准库时,运行报错描述为
ImportError: No module named enum
。产生原因:MaxCompute项目未开启Python 3,默认使用Python 2环境运行MaxCompute UDF,无法识别Python 3的标准库。
解决措施:在调用MaxCompute UDF的SQL语句前增加
set odps.sql.python.version=cp37;
与SQL语句一起提交执行。
问题现象六:运行报错描述为
ModuleNotFoundError: No module named 'six'
。产生原因:Python UDF引入第三方包时,没有将包的路径加入到
sys.path
中,导致第三方包无法正常导入。解决措施:请参见在MaxCompute UDF中运行Scipy,将
include_package_path('six.zip')
修改为sys.path.insert(0, 'work/six.zip')
。
问题现象七:运行报错描述为
failed to get Udf info from xxx.py
。产生原因:编写的UDTF或UDAF代码中,基类的导入写法不正确。例如
import odps.udf.BaseUDTF
或import odps.udf.BaseUDAF
。解决措施:修改为
from odps.udf import BaseUDTF
或from odps.udf import BaseUDAF
。
性能问题
问题现象:运行报错描述为
kInstanceMonitorTimeout
。产生原因:MaxCompute UDF处理时间过长导致超时。默认情况下UDF处理数据的时间有限制,在处理一批(通常情况下为1024条)记录时,必须在1800秒内处理完。这个时间限制并不是针对Worker的总运行时间,而是处理一小批记录的时间。通常情况下SQL处理数据的速率超过了万条/秒,该限制只是为了防止MaxCompute UDF中出现死循环,导致长时间占用CPU资源的情况。
解决措施:
在MaxCompute UDF代码中增加日志,用于检查代码中是否有死循环问题,或者可以在日志里打印时间信息来检查MaxCompute UDF处理单条数据的时长是否符合预期。代码中需要增加如下打印日志相关信息,作业运行成功后,您可以在Logview的StdOut中获取到日志信息。
Python 2环境
sys.stdout.write('your log') sys.stdout.flush()
Python 3环境
print('your log', flush=True)
如果实际计算量很大,MaxCompute UDF预计的运行时间很长,您可以通过调整如下参数避免超时报错。
参数
说明
set odps.function.timeout=xxx;
调整UDF运行超时时长。默认值为1800s。可根据实际情况酌情调大。取值范围为1s~3600s。
set odps.sql.executionengine.batch.rowcount=xxx;
调整MaxCompute一次处理的数据行数。默认值为1024行。可根据实际情况酌情调小。
网络问题
沙箱问题
问题现象:运行报错描述为
RuntimeError: xxx has been blocked by sandbox
。产生原因:Python UDF中的某些函数调用被沙箱阻断了。
解决措施:
在调用Python UDF的SQL语句前,增加
set odps.isolation.session.enable=true;
设置,与SQL语句一起提交执行。使用Python 3 UDF,默认会设置
set odps.isolation.session.enable=true;
。
编码问题
调用MaxCompute UDF运行代码时的常见编码问题如下:
问题现象一:运行报错描述为
SyntaxError: Non-ASCII character '\xe8' in file xxx. on line yyy
。产生原因:MaxCompute UDF对应的Python文件中存在非ASCII编码字符,且运行在Python 2环境中。
解决措施:
在调用MaxCompute UDF的SQL语句前增加
set odps.sql.python.version=cp37;
与SQL语句一起提交,在Python 3环境下运行作业。将Python 2的默认解析器编码方式修改为UTF-8,即在Python文件开头添加如下语句。
import sys reload(sys) sys.setdefaultencoding('utf-8')
问题现象二:调用Python 2 UDF时,运行报错描述为
UnicodeEncodeError: 'ascii' code can't encode characters in position x-y: ordinal not in range(128)
。产生原因:函数签名中返回值类型是STRING,但MaxCompute UDF返回UNICODE类型的Python对象,假设对象名为ret。MaxCompute默认会将返回值ret按照ASCII编码格式转换为STR类型,返回
str(ret)
。当ret本身在ASCII编码范围内时,可以成功转换为STR类型,但如果ret不在ASCII编码范围内时,转换会失败并返回报错。解决措施:在Python代码的
evaluate
方法中增加如下语句。return ret.encode('utf-8')
问题现象三:调用Python 3 UDF时,运行报错描述为
UnicodeDecodeError: 'utf-8' codec can't decode byte xxx in position xxx: invalid continuation byte
。产生原因:函数签名中输入参数类型是STRING,但是调用Python 3 UDF时输入的字符串不能按照UTF-8解码为STR类型的Python对象。
解决措施:
避免向MaxCompute表中写入非UTF-8编码的字符串。
例如,Python 2 UDF返回的Python对象是按GBK编码的STR,可以正常写入MaxCompute表中,但无法被Python 3 UDF读取,Python 2 UDF返回数据时建议转为UTF-8编码后再返回,例如返回
ret.decode('gbk').encode('utf-8')
。在SQL语句中使用内建函数
is_encoding
提前过滤掉非UTF-8编码的数据。代码示例如下。select py_udf(input_col) from example_table where is_encoding(input_col, 'utf-8', 'utf-8') = true;
将Python代码中的函数签名输入参数类型修改为BINARY,并在SQL语句中将STRING类型列转换为BINARY类型作为Python 3 UDF入参。代码示例如下。
select py_udf(cast(input_col as binary)) from example_table;
函数签名问题
调用MaxCompute UDF运行代码时的常见函数签名问题如下:
问题现象一:运行报错描述为
resolve annotation of class xxx for UDTF/UDF/UDAF yyy contains invalid content '<EOF>'
。产生原因:MaxCompute UDF的输入或输出参数为复杂数据类型,但函数签名不合法。
解决方案:修改函数签名中的复杂数据类型写法,确保为合法的函数签名。更多函数签名信息,请参见函数签名及数据类型。
问题现象二:运行报错描述为
TypeError: expected <class 'xxx'> but <class 'yyy'> found, value:zzz
。产生原因:函数签名指定的返回值类型与MaxCompute UDF代码实际返回的数据类型不一致。
解决措施:确认期望返回结果,修改函数签名或MaxCompute UDF代码,确保二者数据类型一致。
问题现象三:运行报错描述为
Semantic analysis exception - evaluate function in class xxx.yyy for user defined function zz does not match annotation ***->***
。产生原因:函数签名中指定的入参个数与MaxCompute UDF代码中对应方法的入参个数不一致。
解决措施:确认实际入参个数,修改函数签名或MaxCompute UDF代码,确保二者入参个数一致。
第三方包问题
问题现象:运行报错描述为
GLIBCXX_x.x.x not found
。产生原因:报错的so链接库文件依赖的GLIBCXX版本高于MaxCompute本身支持的版本。GLIBC、CXXABI同理。
解决措施:使用兼容的WHEEL包或在兼容的环境中重新编译so链接库文件。MaxCompute支持的二进制可执行文件或so链接库文件依赖的最大版本如下。
GLIBC <= 2.17 CXXABI <= 1.3.8 GLIBCXX <= 3.4.19 GCC <= 4.2.0
UDTF相关问题
问题现象:运行报错描述为
Semantic analysis exception - expect 2 aliases but have 0
。产生原因:Python UDTF代码中没有指定输出列名。
解决措施:您可以在调用Python UDTF的SELECT语句中通过
as
子句给出列名。命令示例如下。select my_udtf(col0, col1) as (ret_col0, ret_col1, ret_col2) from tmp1;
UDAF相关问题
问题现象一:运行报错描述为
Script exception - ValueError: unmarshallable object
。产生原因:Python UDAF代码中的
buffer
不是Marshal对象。解决措施:为
buffer
赋值时,需要确保值为Marshal对象。假设Python UDAF中要使用两个buffer
,类型分别为LIST和DICT,则new_buffer
方法中应该写为return [list(), dict()]
。在iterate/merge/terminate
方法中使用buffer/pbuffer
时,LIST类型的buffer
对应buffer[0]/pbuffer[0]
,DICT类型的buffer
对应buffer[1]/pbuffer[1]
。如果buffer
的元素为LIST或DICT,这些元素也必须是Marshal对象。
问题现象二:运行报错描述为
Python UDAF buffer size overflowed: 2821486749
。产生原因:Python UDAF中的
buffer
经过Marshal
处理后的大小超过2 GB,用户使用buffer
的方式有误,buffer
的大小不应该随数据量递增。解决措施:重新设计Python UDAF的逻辑,
buffer
的大小不应该随数据量递增。例如声明了一个buffer
是list
,iterate
和merge
阶段不能一直往buffer
里增加数据。更多Python UDAF信息,请参见UDAF概述。