本文为您介绍使用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>;命令,检查输出结果中的ClassResources的正确性。

        如果不正确,需要执行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 failedodps.distcache.DistributedCacheErrorfuxi 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语句一起提交执行。
  • 问题现象六:运行报错描述为failed to get Udf info from xxx.py
    • 产生原因:编写的UDTF或UDAF代码中,基类的导入写法不正确。例如import odps.udf.BaseUDTFimport odps.udf.BaseUDAF
    • 解决措施:修改为from odps.udf import BaseUDTFfrom odps.udf import BaseUDAF

性能问题

  • 问题现象:运行报错描述为kInstanceMonitorTimeout
  • 产生原因:MaxCompute UDF处理时间过长导致超时。默认情况下UDF处理数据的时间是有限制,在处理一批(通常情况下为1024条)记录时,必须在1800秒内处理完。这个时间限制并不是针对Worker的总运行时间,而是处理一小批记录的时间。通常情况下SQL处理数据的速率超过了万条/秒,该限制只是为了防止MaxCompute UDF中出现死循环,导致长时间占用CPU资源的情况。
  • 解决措施:
    • 在MaxCompute UDF代码中增加日志,用于检查代码中是否有死循环问题,或者可以在日志里打印时间信息来检查MaxCompute UDF处理单条数据的时长是否符合预期。代码中需要增加如下打印日志相关信息,作业运行成功后,您可以在LogviewStdOut中获取到日志信息。
      • 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行。可根据实际情况酌情调小。

网络问题

  • 问题现象:调用MaxCompute UDF访问外网时,代码运行会报错
  • 产生原因:MaxCompute UDF不支持访问外网。
  • 解决措施:请根据业务情况填写并提交网络连接申请表单,MaxCompute技术支持团队会及时联系您完成网络开通操作。表单填写指导,请参见网络开通流程

沙箱问题

  • 问题现象:运行报错描述为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 charactor '\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的大小不应该随数据量递增。例如声明了一个bufferlistiteratemerge阶段不能一直往buffer里增加数据。更多Python UDAF信息,请参见UDAF概述