本文介绍Spark作业异常的排查方法和解决方案。

内存问题引起的报错

Container killed by YARN for exceeding memory limits

  • 报错原因:提交App时申请的内存量较低,但JVM启动占用了更多的内存,超过了自身的申请量,导致被YARN NodeManager异常终止。特别是Spark类型作业,可能会占用多的堆外内存,很容易被异常终止。
  • 解决方案:在EMR控制台中Spark服务的配置页面,调大spark.driver.memoryOverheadspark.executor.memoryOverhead的值。

Spark任务读写OSS文件时出现Container killed by YARN for exceeding memory limits

  • 报错原因:可能是由于读写OSS时使用的内存缓存过大。
  • 解决方案:增大Spark Executor内存。如果无法增大Spark Executor内存,可以在EMR控制台Hadoop-Common服务配置页面的core-site.xml页签下,调整以下和OSS相关的配置参数:
    • fs.oss.read.readahead.buffer.count:0
    • fs.oss.read.buffer.size:16384
    • fs.oss.write.buffer.size:16384
    • fs.oss.memory.buffer.size.max.mb:512

Error: Java heap space

  • 报错原因:Spark作业Task处理的数据量较大,但Executor JVM申请的内存量不足,从而出现java.lang.OutOfMemoryError报错。
  • 解决方案:在EMR控制台中Spark服务的配置页面,针对不同的场景调大spark.executor.memoryspark.driver.memory的值。

读取Snappy文件时报错OutOfMemoryError

针对Spark作业,您可以在EMR控制台中Spark服务配置页面的spark-defaults.conf页签下,新增spark.hadoop.io.compression.codec.snappy.native=true配置。

其他Spark Driver OOM内存不足场景

您可以按照以下方案解决:
  • 在EMR控制台中Spark服务的配置页面,调大spark.driver.memory的值。
  • 查看是否有collect等把数据拉取到driver的操作,如果collect的数据比较大,建议使用foreachPartitions在executor进行操作,移出collect相关代码。
  • 设置spark.sql.autoBroadcastJoinThreshold=-1

其他Spark Executor OOM内存不足场景

您可以按照以下方案解决:
  • 在EMR控制台中Spark服务的配置页面,调大spark.executor.memory的值。
  • 调小spark.executor.cores的值。
  • 调大spark.default.parallelismspark.sql.shuffle.partitions的值。

文件格式报错

Hive或Impala作业读取Spark导入的Parquet表报错

  • 具体报错:Failed with exception java.io.IOException:org.apache.parquet.io.ParquetDecodingException: Can not read value at 0 in block -1 in file xxx
  • 报错原因:由于Hive和Spark在Decimal类型上使用了不同的转换方式写入Parquet,导致Hive无法正确读取Spark导入的数据。
  • 解决方案:已使用Spark导入的数据,如果需要被Hive或Impala使用,建议在EMR控制台中Spark服务配置页面的spark-defaults.conf页签下,增加spark.sql.parquet.writeLegacyFormat=true配置后重新导入数据。

Shuffle报错

java.lang.IllegalArgumentException: Size exceeds Integer.MAX_VALUE

  • 报错原因:在Spark Shuffle时,Partition数量过少使得Block Size超过Integer.MAX_VALUE的值。
  • 解决方案:建议增加Partition个数,在控制台中调大spark.default.parallelismspark.sql.shuffle.partitions的值,或者在Shuffle前执行Repartition。

外部数据源问题引起的报错

java.sql.SQLException: No suitable driver found for jdbc:mysql:xxx

mysql-connector-java版本过低,请替换为较新版本(例如5.1.48以上版本)。

连接RDS报错Invalid authorization specification, message from server: ip not in whitelist

检查RDS的白名单设置,将EMR集群节点(需要包括master、core、task等节点组)的内网地址添加到RDS的白名单中。

其他异常报错

Spark UI中一直没有分配Job,或者Job全部结束,但是Spark任务长时间不结束

进入Spark UI,查看Executor页面,分析driver的thread dump。如果很多orc相关的线程,则在Spark任务启动前设置--conf spark.hadoop.hive.exec.orc.split.strategy=BI,再重新启动Spark任务。如果是Spark2.x版本,还需要查看spark.sql.adaptive.enabled是否为true,如果为true,需要修改为false。

Spark任务一直不结束,同时无法进入Spark UI

  • 报错原因:通常是driver内存不足,fullgc严重导致的。
  • 解决方案:增大spark.driver.memory的值。

Spark使用代码读取Hive数据时,出现NoSuchDatabaseException: Database 'xxx' not found

  • 查看初始化SparkSession的时候,是否执行了.enableHiveSupport()。如果没有执行,则需要手动执行。
  • 查看是否有代码执行了new SparkContext()。如果有则移出相关代码,从SparkSession中获取SparkContext。

Spark作业出现java.lang.ClassNotFoundException

根据Class的信息找到对应的JAR包,并通过以下方案处理:
  • 方案1:提交Spark任务时,使用--jars把JAR包提交上去。
  • 方案2:使用spark.driver.extraclasspathspark.executor.extraclasspath把JAR包的路径写入classpath,这个方案需要EMR集群每个节点上都有对应的JAR包。