You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Synapse中使用PySpark UDF时遇无法序列化_thread.RLock对象错误

问题

在Synapse中使用PySpark创建DataFrame,通过自定义函数(UDF)添加计算列时,想在函数异常块中记录错误信息,但调用logger.error时触发了TypeError: cannot pickle '_thread.RLock' object错误。

代码片段

logger = sc._jvm.org.apache.log4j.LogManager.getLogger("com.test.test")
def log_test(arg):
    try:
        return 1/arg
    except Exception as e:
        logger.error("Divided by zero error occurred")

udf_log_test = udf(lambda col:log_test(col) if col != 0 else 0, IntegerType())
    
df = df.withColumn('TestColumn', udf_log_test(lit(0)))

错误栈信息

Traceback (most recent call last):
      File "/databricks/spark/python/pyspark/serializers.py", line 527, in dumps
        return cloudpickle.dumps(obj, pickle_protocol)
      File "/databricks/spark/python/pyspark/cloudpickle/cloudpickle_fast.py", line 73, in dumps
        cp.dump(obj)
      File "/databricks/spark/python/pyspark/cloudpickle/cloudpickle_fast.py", line 602, in dump
        return Pickler.dump(self, obj)
    TypeError: cannot pickle '_thread.RLock' object
    PicklingError: Could not serialize object: TypeError: cannot pickle '_thread.RLock' object

TypeError                                 Traceback (most recent call last)
File /databricks/spark/python/pyspark/serializers.py:527, in CloudPickleSerializer.dumps(self, obj)
    526 try:
--> 527     return cloudpickle.dumps(obj, pickle_protocol)
    528 except pickle.PickleError:

File /databricks/spark/python/pyspark/cloudpickle/cloudpickle_fast.py:73, in dumps(obj, protocol, buffer_callback)
     70 cp = CloudPickler(
     71     file, protocol=protocol, buffer_callback=buffer_callback
     72 )
--> 73 cp.dump(obj)
     74 return file.getvalue()

File /databricks/spark/python/pyspark/cloudpickle/cloudpickle_fast.py:602, in CloudPickler.dump(self, obj)
    601 try:
--> 602     return Pickler.dump(self, obj)
    603 except RuntimeError as e:

TypeError: cannot pickle '_thread.RLock' object

During handling of the above exception, another exception occurred:

PicklingError                             Traceback (most recent call last)
File <command-109665656057519>:1
----> 1 df = df.withColumn('TestColumn', udf_log_test(lit(0)))

File /databricks/spark/python/pyspark/sql/udf.py:322, in UserDefinedFunction._wrapped.<locals>.wrapper(*args)
    320 @functools.wraps(self.func, assigned=assignments)
    321 def wrapper(*args: "ColumnOrName") -> Column:
--> 322     return self(*args)

File /databricks/spark/python/pyspark/sql/udf.py:299, in UserDefinedFunction.__call__(self, *cols)
    297         sc.profiler_collector.add_profiler(id, memory_profiler)
    298 else:
--> 299     judf = self._judf
    300     jPythonUDF = judf.apply(_to_seq(sc, cols, _to_java_column))
    301 return Column(jPythonUDF)

File /databricks/spark/python/pyspark/sql/udf.py:219, in UserDefinedFunction._judf(self)
    212 @property
    213 def _judf(self) -> JavaObject:
    214     # It is possible that concurrent access, to newly created UDF,
    215     # will initialize multiple UserDefinedPythonFunctions.
    216     # This is unlikely, doesn't affect correctness,
    217     # and should have a minimal performance impact.
    218     if self._judf_placeholder is None:
--> 219         self._judf_placeholder = self._create_judf(self.func)
    220     return self._judf_placeholder

File /databricks/spark/python/pyspark/sql/udf.py:228, in UserDefinedFunction._create_judf(self, func)
    225 spark = SparkSession._getActiveSessionOrCreate()
    226 sc = spark.sparkContext
--> 228 wrapped_func = _wrap_function(sc, func, self.returnType)
    229 jdt = spark._jsparkSession.parseDataType(self.returnType.json())
    230 assert sc._jvm is not None

File /databricks/spark/python/pyspark/sql/udf.py:51, in _wrap_function(sc, func, returnType)
     47 def _wrap_function(
     48     sc: "SparkContext", func: Callable[..., Any], returnType: "DataTypeOrString"
     49 ) -> JavaObject:
     50     command = (func, returnType)
--> 51     pickled_command, broadcast_vars, env, includes = _prepare_for_python_RDD(sc, command)
     52     assert sc._jvm is not None
     53     return sc._jvm.SimplePythonFunction(
     54         bytearray(pickled_command),
     55         env,
   (...)
     60         sc._javaAccumulator,
     61     )

File /databricks/spark/python/pyspark/rdd.py:5248, in _prepare_for_python_RDD(sc, command)
   5245 def _prepare_for_python_RDD(sc: "SparkContext", command: Any) -> Tuple[bytes, Any, Any, Any]:
   5246     # the serialized command will be compressed by broadcast
   5247     ser = CloudPickleSerializer()
-> 5248     pickled_command = ser.dumps(command)
   5249     assert sc._jvm is not None
   5250     if len(pickled_command) > sc._jvm.PythonUtils.getBroadcastThreshold(sc._jsc):  # Default 1M
   5251         # The broadcast will have same life cycle as created PythonRDD

File /databricks/spark/python/pyspark/serializers.py:537, in CloudPickleSerializer.dumps(self, obj)
    535     msg = "Could not serialize object: %s: %s" % (e.__class__.__name__, emsg)
    536 print_exec(sys.stderr)
-> 537 raise pickle.PicklingError(msg)

PicklingError: Could not serialize object: TypeError: cannot pickle '_thread.RLock' object

解决方案

问题原因

PySpark的UDF需要序列化后分发到集群的各个Executor节点执行,但你在Driver端创建的Log4j logger对象包含_thread.RLock(线程锁),这个对象无法被序列化,导致UDF在序列化时失败。

解决方法1:在UDF内部延迟初始化Logger

不在Driver端提前创建logger,而是在UDF内部每次执行时初始化,这样每个Executor节点会自己创建独立的logger实例,避免序列化问题:

from pyspark.sql.functions import udf, lit
from pyspark.sql.types import IntegerType

def log_test(arg):
    try:
        return 1 / arg
    except Exception as e:
        # 延迟初始化logger,确保在Executor端创建
        logger = sc._jvm.org.apache.log4j.LogManager.getLogger("com.test.test")
        logger.error(f"Divided by zero error occurred: {str(e)}")
        return 0  # 必须返回对应类型的默认值,避免类型不匹配

udf_log_test = udf(log_test, IntegerType())

df = df.withColumn('TestColumn', udf_log_test(lit(0)))

解决方法2:使用Python标准logging模块

改用Python自带的logging模块,该模块的logger对象可以正常序列化,日志会输出到Executor的本地日志中:

import logging
from pyspark.sql.functions import udf, lit
from pyspark.sql.types import IntegerType

# 在Driver端配置日志级别
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger("com.test.test")

def log_test(arg):
    try:
        return 1 / arg
    except Exception as e:
        logger.error(f"Divided by zero error occurred: {str(e)}")
        return 0

udf_log_test = udf(log_test, IntegerType())

df = df.withColumn('TestColumn', udf_log_test(lit(0)))

注意事项

  • 方法1的Log4j日志会集成到Spark的统一日志系统中,日志可以通过Spark的日志收集工具查看;
  • 方法2的Python日志需要确保Executor节点的日志配置正确,默认会输出到Executor的本地日志文件。

内容的提问来源于stack exchange,提问作者Anil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 09:03:14