本文列举MaxCompute Spark使用过程中的其他常见问题及解决方法。
项目工程自检
提交作业前,请参考LogView,并按照以下清单逐项检查。
pom.xml检查:
spark-xxxx_${scala.binary.version}依赖的scope必须设置为provided。<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency>主类代码中硬编码
spark.master:如果使用
yarn-cluster方式提交,代码里如果还有local[N]的配置将会出错。val spark = SparkSession .builder() .appName("SparkPi") // 如果是yarn-cluster方式提交,不要在代码中设置以下配置 // .config("spark.master", "local[4]") .getOrCreate()主类Scala代码必须是object不是class:
在IDEA中创建文件时需注意使用
object,否则main函数无法加载。object SparkPi { // 必须得是object,有的用户在IDEA创建文件的时候会不小心写成class,这样子main函数是无法加载的 def main(args: Array[String]) { val spark = SparkSession .builder() .appName("SparkPi") .getOrCreate()不推荐在代码中hard-code配置:
在做Local测试时,开发者习惯将MaxCompute的配置hard-code在代码里,但有一些配置写在代码中无法生效。强烈建议使用
yarn-cluster方式提交时,把配置项都写在spark-defaults.conf里。val spark = SparkSession .builder() .appName("SparkPi") .config("key1", "value1") .config("key2", "value2") .config("key3", "value3") ... // 开发者在做local测试的时候习惯把MaxCompute的配置hard-code在代码里,但是有一些配置写在代码里是无法生效的 .getOrCreate() 强烈建议yarn-cluster方式提交的时候,把配置项都写在spark-defaults.conf里面
User signature does not match
异常信息:
Invalid signature value - User signature does not match解决方法:一般是
spark-defaults.conf里的spark.hadoop.odps.access.id和spark.hadoop.odps.access.key填写有误。请在阿里云AccessKey管理页面再次确认,确保复制粘贴过程中没有出错。
You have NO privilege 'odps:CreateResource'
异常信息
com.aliyun.odps.OdpsException: ODPS-0420095: Access Denied - Authorization Failed [4019], You have NO privilege 'odps:CreateResource' on {acs:odps:*:projects/*}解决方法
一般是用来提交Spark作业的AccessKey对应的云账号或子账号没有对应权限。请联系项目Owner授权相关权限:
GRANT CreateInstance, CreateResource, ReadResource ON PROJECT <projectName> TO USER <userId>;详细授权语法参考MaxCompute权限。
The task is not in release range: CUPID
异常信息:
The task is not in release range: CUPID解决方法:一般是因为当前地域未开启MaxCompute Spark支持,请联系技术支持确认该地域是否支持Spark服务。
No space left on device
异常信息:
No space left on device解决方法:Spark使用网盘作为本地存储,Driver和每个Executor各有一个,Shuffle数据以及BlockManager溢出的数据均存储在网盘上。网盘大小通过参数
spark.hadoop.odps.cupid.disk.driver.device_size控制,默认20 GB,最大100 GB。如果调整到100 GB仍然出现此错误,需要分析具体原因:数据倾斜,在Shuffle或Cache过程中数据集中分布在某些Block。
缩小单个Executor的并发(
spark.executor.cores),增加Executor的数量(spark.executor.instances)。
ClassNotFound类错误
异常信息:
java.lang.ClassNotFoundException: xxxx.xxx.xxxxx解决方法:
执行
jar -tf <作业jar包> | grep <类名称>,确认提交的JAR包中是否包含该类定义。检查
pom.xml中的依赖是否正确,确保提交的是带shaded关键字的JAR包(表示已将工程中的依赖全部打包)。提交Spark作业的时候,请记得提交带
shaded关键字的包,shaded关键字表示把工程中的依赖都全部打包进来。
Jar包版本冲突类错误
异常信息:
User class threw exception: java.lang.NoSuchMethodError解决方法:
在
$SPARK_HOME/jars路径下查找异常类所在的JAR包,使用命令grep <异常类名> $SPARK_HOME/jars/*.jar可定位到三方库的坐标及版本。在Spark作业工程根目录下查看所有依赖:
mvn dependency:tree找到冲突依赖后,使用Maven dependency exclusions排除冲突包,然后重新编译提交。
Shutdown hook called before final status was reported
异常信息:提交作业后很快报错:
App Status: SUCCEEDED, diagnostics: Shutdown hook called before final status was reported.解决方法:由于提交到集群的user main函数没有通过ApplicationMaster申请集群资源直接退出了。
创建SparkContext;
在代码中设置
spark.master为local。
中文乱码
异常信息:运行Spark作业打印的中文出现乱码。
解决方法
添加如下配置:
--conf spark.executor.extraJavaOptions="-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8" --conf spark.driver.extraJavaOptions="-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"如果是PySpark作业,还需设置以下参数:
spark.yarn.appMasterEnv.PYTHONIOENCODING=utf8 spark.executorEnv.PYTHONIOENCODING=utf8另外在python脚本的最前面加上如下代码:
# -*- coding: utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8')
找不到Table或视图
异常信息:
Table or view not found: xxx解决方法:
首先检查报错的表在项目中是否真的存在。如果确实存在,检查是否开启了Hive的Catalog配置,需要去掉Hive配置。例如一个常见的导致报错的PySpark写法:
# 错误写法 spark = SparkSession.builder.appName(app_name).enableHiveSupport().getOrCreate() # 正确写法:去掉enableHiveSupport spark = SparkSession.builder.appName(app_name).getOrCreate()
com.aliyun.odps.cupid.CupidException: Row Not Found
异常信息:
com.aliyun.odps.cupid.CupidException: Row Not Found解决方法:该错误一般是由于用户代码中配置的项目名称,和实际运行作业的项目名称不一致导致的。
spark.hadoop.odps.project.name配置应当是运行Spark作业的项目名称,而不是访问表所在的项目名称。
如何把JAR包当作资源引用
spark.hadoop.odps.cupid.resources可以将JAR包当作资源传到项目中。
例如:spark.hadoop.odps.cupid.resources=projectname.xx0.jar,projectname.xx1.jar。这种方式支持多个项目共享,只要设置好权限即可。
如何调节读MaxCompute表的并发度
配置Split大小:spark.hadoop.odps.input.split.size,单位MB,默认256。如果表的数据量太小,也可以把数据读出来后通过repartition来调大后续的并发。
如何在代码中使用MaxCompute Java SDK
确认以provided的方式添加了cupid-sdk的依赖,然后直接通过CupidSession.get().odps()获取Odps对象。
如何Kill一个Spark任务
直接在Spark客户端或DataWorks任务提交界面执行Ctrl+C无法Kill一个Spark任务。通常通过以下两种方式Kill正在运行的Spark任务:
通过MaxCompute客户端执行
kill <instanceId>;通过DataWorks界面执行Stop。
com.aliyun.odps.cupid.ExceedMaxOpenFileException
异常信息:动态分区插入报错。
com.aliyun.odps.cupid.ExceedMaxOpenFileException: NumPartitions: 1024, maxPartSpecNum: 1000, openFiles: 98, maxOpenFiles: 100000, exceeded Max limit解决方法
出现此报错有两种情况:
当
NumPartitions × openFiles > maxOpenFiles时报此错误。maxOpenFiles的默认参数通过spark.hadoop.odps.cupid.open.file.max控制,调节该参数使其大于NumPartitions × openFiles即可。当
openFiles > maxPartSpecNum时报此错误。maxPartSpecNum的默认参数通过spark.hadoop.odps.cupid.partition.spec.max控制,调节该参数使其大于openFiles即可。
作业执行抛出异常:***.so: cannot open shared object file: No such file or directory
异常信息:作业执行抛出异常
***.so: cannot open shared object file: No such file or directory解决方法
MaxCompute Spark客户端:
从公网下载对应的依赖文件,提交作业时通过参数
--files /path/to/[lib名]将对应的依赖文件加载至Driver与Executor的工作目录。DataWorks Spark节点:
从公网下载对应的依赖文件,通过DataWorks添加对应的依赖资源(创建MaxCompute资源),然后在作业提交时补充参数
spark.hadoop.odps.cupid.resources。
由于上传的依赖资源以项目名称为前缀,需要重命名上传的Resource名称以去掉项目名称的前缀,这样才可以正确加载依赖。