Spark 3.5.0在Apple Silicon M1 Pro上将RDD转DataFrame时卡顿
问题背景
在Apple Silicon M1 Pro芯片上运行Spark 3.5.0的PySpark代码时出现卡顿,此前Spark 3.4.2运行正常。尝试过Python 3.9+Spark 3.4.2、Python 3.10+Spark 3.4.2、Python 3.9+Spark 3.5.0等版本组合,问题仍存在。Spark UI和jstack日志显示Python与Java之间存在阻塞,而spark-shell和SparkR运行相同逻辑正常。
日志分析
从提供的jstack日志可以看到两个关键线程状态:
- Idle Worker Monitor for python3:处于
BLOCKED状态,等待获取PythonWorkerFactory对象的锁 - Executor task launch worker:持有
PythonWorkerFactory的锁,但卡在java.net.SocketInputStream.socketRead0调用上,说明Java端正在等待Python worker进程的响应,但Python worker没有返回数据,导致整个流程阻塞。
这种情况通常是Python worker进程启动失败、无响应,或者Java与Python进程之间的通信出现问题。
解决方案
1. 强制使用IPv4通信
Apple Silicon上的Java默认可能优先使用IPv6,而Spark的Python worker通信在IPv6环境下可能存在兼容性问题。可以通过以下配置强制使用IPv4:
- 在PySpark代码中添加配置:
spark.conf.set("spark.driver.host", "127.0.0.1") spark.conf.set("spark.driver.bindAddress", "127.0.0.1") - 或者启动PySpark时设置环境变量:
export PYSPARK_SUBMIT_ARGS="--driver-java-options '-Djava.net.preferIPv4Stack=true' pyspark-shell"
2. 禁用Python Worker复用
Spark默认会复用Python worker进程,新版本中可能在ARM架构下存在复用逻辑的bug,尝试禁用复用:
spark.conf.set("spark.python.worker.reuse", "false")
3. 确保使用原生ARM64版本的Python
避免使用Rosetta转译的x86版本Python,Spark 3.5.0对ARM64的Python兼容性要求更严格。可以通过以下命令确认Python架构:
python3 -c "import platform; print(platform.machine())"
输出应为arm64,如果是x86_64则需要重新安装原生ARM64版本的Python(比如通过pyenv或Homebrew安装)。
4. 调整Python Worker内存配置
如果Python worker内存不足导致崩溃,也会出现无响应的情况,尝试增加worker内存:
spark.conf.set("spark.python.worker.memory", "2g")
5. 查看Python Worker日志
开启Python worker的DEBUG日志,排查worker进程的具体错误:
spark.conf.set("spark.python.worker.log.level", "DEBUG")
日志文件通常位于Spark的临时目录下,可以通过spark.local.dir配置指定目录,查看worker的输出日志定位具体问题。
6. 回退到Spark 3.4.2并确认环境一致性
如果上述方案无效,回退到之前正常运行的Spark 3.4.2,检查环境变量(如JAVA_HOME、PYSPARK_PYTHON、PYSPARK_DRIVER_PYTHON)是否与之前一致,避免因环境变更导致的问题。
内容的提问来源于stack exchange,提问作者tonny2v

