本文為您介紹使用Python語言編寫的MaxCompute UDF的常見問題。
類或資源問題
調用MaxCompute UDF運行代碼時的常見類或資源問題如下:
問題現象一:運行報錯描述為
function 'xxx' cannot be resolved
。產生原因:
原因一:調用MaxCompute UDF運行代碼時,所處的項目不正確。即MaxCompute UDF不在MaxCompute項目中。例如MaxCompute UDF注冊到了開發(fā)項目,但卻在生產項目執(zhí)行調用操作。
原因二:MaxCompute UDF的類不正確或資源不正確。
原因三:MaxCompute UDF依賴的資源類型不正確。例如PY文件,資源類型是PY,但MaxCompute UDF代碼中
get_cache_file
需要的類型是FILE。原因四:MaxCompute UDF依賴的資源不是最新的。當您通過DataWorks上傳MaxCompute資源時,從DataWorks同步至MaxCompute會存在延時情況,非最新資源。
原因五:Python環(huán)境版本不正確。MaxCompute默認采用Python 2運行作業(yè),當Python代碼中存在非ASCII編碼字符時,運行會報錯。
解決措施:
原因一的解決措施:在報錯的項目下通過MaxCompute客戶端執(zhí)行
list functions;
命令,確保MaxCompute UDF是真實存在的。原因二的解決措施:通過MaxCompute客戶端執(zhí)行
desc function <function_name>;
命令,檢查輸出結果中的Class及Resources的正確性。如果不正確,需要執(zhí)行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注冊函數,其中package_to_class為Python腳本名.類名,resource_list為MaxCompute中需要引用的所有文件資源、表資源、壓縮包資源或第三方包。更多注冊函數操作,請參見注冊函數。
原因三的解決措施:通過MaxCompute客戶端執(zhí)行
desc resource <resource_name>;
命令,檢查輸出結果中的Type的正確性。如果資源類型不正確,可執(zhí)行add <file_type> <file_name>;
重新添加資源。如果MaxCompute UDF代碼中的引用資源方式為
get_cache_file
,表明引用的是文件資源,資源類型必須為FILE。如果MaxCompute UDF代碼中的引用資源方式為
get_cache_table
,表明引用的是表資源,資源類型必須為TABLE。如果MaxCompute UDF代碼中的引用資源方式為
get_cache_archive
,表明引用的是壓縮包資源,資源類型必須為ARCHIVE。
更多上傳資源操作,請參見添加資源。
原因四的解決措施:通過MaxCompute客戶端執(zhí)行
desc resource <resource_name>;
命令,檢查輸出結果中的LastModifiedTime,確保為最近一次變更的時間。原因五的解決措施:在Python代碼頭部增加
#coding:utf-8
或# -*- coding: utf-8 -*-
編碼聲明,或在調用MaxCompute UDF的SQL語句前增加set odps.sql.python.version=cp37;
與SQL語句一起提交,在Python 3環(huán)境下運行作業(yè)。
問題現象二: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格式解壓,導致解壓失敗報錯。
原因四:同一個作業(yè)中有兩個UDF依賴了不同項目下的同名資源。
解決措施:
原因一的解決措施:通過MaxCompute客戶端執(zhí)行
desc function <function_name>;
命令,檢查輸出結果中的Resources是否包含報錯信息中的壓縮資源包。如果不存在,可執(zhí)行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注冊函數,并在resource_list中加上缺失的壓縮包資源。更多注冊函數操作,請參見注冊函數。
原因二的解決措施:通過MaxCompute客戶端執(zhí)行
desc resource <resource_name>;
命令,檢查輸出結果中的Type是否為ARCHIVE。如果不是ARCHIVE類型,可執(zhí)行
add archive <file_name>;
命令重新上傳資源。更多上傳資源操作,請參見添加資源。
原因三的解決措施:通過MaxCompute客戶端執(zhí)行
desc function <function_name>;
命令,檢查輸出結果中的Resources中的壓縮包資源名及后綴是否與實際文件名、后綴一致。如果不一致,可執(zhí)行
add archive <file_name>;
命令重新上傳資源,file_name必須與實際壓縮包資源名及后綴保持一致。原因四的解決措施:排查作業(yè)依賴的所有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客戶端執(zhí)行
desc function <function_name>;
命令,檢查輸出結果中的Resources是否包含報錯信息中的表資源。如果不存在,可執(zhí)行
create function <function_name> as <'package_to_class'> using <'resource_list'>;
命令重新注冊函數,并在resource_list中加上缺失的表資源。更多注冊函數操作,請參見注冊函數。
原因二的解決措施:通過MaxCompute客戶端執(zhí)行
desc resource <resource_name>;
命令,檢查輸出結果中的Type是否為TABLE。如果不是TABLE類型,可執(zhí)行
add table <table_name>;
命令重新上傳表資源。更多上傳資源操作,請參見添加資源。
問題現象四:MaxCompute UDF引用第三方包時,運行報錯描述為
ImportError: No module named 'xxx'
。產生原因:
原因一:第三方包的資源類型不正確,非ARCHIVE。
原因二:注冊MaxCompute UDF時,未同步指定第三方包。
原因三:MaxCompute UDF代碼中未添加第三方包路徑。
原因四:第三方包為WHEEL包,但后綴不正確。您需要根據Python環(huán)境版本下載對應的WHEEL文件。
原因五:第三方包不是WHEEL包,且非純Python包,但包中存在setup.py文件。
原因六:MaxCompute UDF對應的PY文件名稱與需要引用的第三方模塊的名稱沖突。例如MaxCompute UDF對應的Python文件是A.py,import A時默認會導入A.py而不是三方包里的模塊。
解決措施:
原因一的解決措施:通過MaxCompute客戶端執(zhí)行
desc resource <resource_name>;
命令,檢查輸出結果中的Type是否為ARCHIVE。如果不是ARCHIVE類型,可執(zhí)行
add archive <file_name>;
命令重新上傳資源。更多上傳資源操作,請參見添加資源。
原因二的解決措施:通過MaxCompute客戶端執(zhí)行
desc function <function_name>;
命令,檢查輸出結果中的Resources是否包含第三方包。如果不包含,可執(zhí)行
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執(zhí)行路徑的相對路徑
./work/
中。原因四的解決措施:Python 2和Python 3環(huán)境對應的WHEEL文件不相同,Python 2要求WHEEL文件名稱中包含
cp27-cp27m-manylinux1_x86_64
,Python 3要求WHEEL文件名稱中包含cp37-cp37m-manylinux1_x86_64
,請下載合適的WHEEL文件。下載的WHEEL文件可以直接修改后綴為.zip,不需要對WHEEL文件再次打包生成ZIP文件。原因五的解決措施:需要先在與MaxCompute兼容的環(huán)境下將setup.py編譯生成WHEEL包,然后再執(zhí)行上傳資源及注冊函數操作。更多編譯第三方包信息,請參見使用需要編譯的第三方包。
原因六的解決措施:修改MaxCompute UDF對應的Python文件名稱。
問題現象五:MaxCompute UDF引用Python 3的標準庫時,運行報錯描述為
ImportError: No module named enum
。產生原因:MaxCompute項目未開啟Python 3,默認使用Python 2環(huán)境運行MaxCompute UDF,無法識別Python 3的標準庫。
解決措施:在調用MaxCompute UDF的SQL語句前增加
set odps.sql.python.version=cp37;
與SQL語句一起提交執(zhí)行。
問題現象六:運行報錯描述為
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中出現死循環(huán),導致長時間占用CPU資源的情況。
解決措施:
在MaxCompute UDF代碼中增加日志,用于檢查代碼中是否有死循環(huán)問題,或者可以在日志里打印時間信息來檢查MaxCompute UDF處理單條數據的時長是否符合預期。代碼中需要增加如下打印日志相關信息,作業(yè)運行成功后,您可以在Logview的StdOut中獲取到日志信息。
Python 2環(huán)境
sys.stdout.write('your log') sys.stdout.flush()
Python 3環(huán)境
print('your log', flush=True)
如果實際計算量很大,MaxCompute UDF預計的運行時間很長,您可以通過調整如下參數避免超時報錯。
參數
說明
set odps.function.timeout=xxx;
調整UDF運行超時時長。默認值為1800s??筛鶕嶋H情況酌情調大。取值范圍為1s~3600s。
set odps.sql.executionengine.batch.rowcount=xxx;
調整MaxCompute一次處理的數據行數。默認值為1024行??筛鶕嶋H情況酌情調小。
網絡問題
沙箱問題
問題現象:運行報錯描述為
RuntimeError: xxx has been blocked by sandbox
。產生原因:Python UDF中的某些函數調用被沙箱阻斷了。
解決措施:
在調用Python UDF的SQL語句前,增加
set odps.isolation.session.enable=true;
設置,與SQL語句一起提交執(zhí)行。使用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環(huán)境中。
解決措施:
在調用MaxCompute UDF的SQL語句前增加
set odps.sql.python.version=cp37;
與SQL語句一起提交,在Python 3環(huán)境下運行作業(yè)。將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包或在兼容的環(huán)境中重新編譯so鏈接庫文件。MaxCompute支持的二進制可執(zhí)行文件或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概述。