其他问题

更新时间:
复制为 MD 格式

本文列举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.idspark.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仍然出现此错误,需要分析具体原因:

    • 数据倾斜,在ShuffleCache过程中数据集中分布在某些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.masterlocal

中文乱码

  • 异常信息:运行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

  • 解决方法

    首先检查报错的表在项目中是否真的存在。如果确实存在,检查是否开启了HiveCatalog配置,需要去掉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名]将对应的依赖文件加载至DriverExecutor的工作目录。

    • DataWorks Spark节点

      从公网下载对应的依赖文件,通过DataWorks添加对应的依赖资源(创建MaxCompute资源),然后在作业提交时补充参数spark.hadoop.odps.cupid.resources

    由于上传的依赖资源以项目名称为前缀,需要重命名上传的Resource名称以去掉项目名称的前缀,这样才可以正确加载依赖。