本文為您介紹使用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概述。