我在AWS EMR上运行5节点Spark群集,每个群集大小为m3.xlarge(1个主4个从属)。我成功地运行了一个146Mb的bzip2压缩CSV文件,结果得到了完美的聚合结果。
现在我正在尝试在此群集上处理~5GB bzip2 CSV文件,但我收到此错误:
16/11/23 17:29:53 WARN TaskSetManager:阶段6.0中的丢失任务49.2(TID xxx,xxx.xxx.xxx.compute.internal):ExecutorLostFailure(执行者16退出由其中一个正在运行的任务引起)原因:由于超出内存限制而被YARN杀死的容器。使用10.4 GB的10.4 GB物理内存。考虑提升spark.yarn.executor.memoryOverhead。
我很困惑为什么我在~75GB群集上获得~10.5GB内存限制(每3m.xlarge实例15GB)...
这是我的EMR配置:
[
{
"classification":"spark-env",
"properties":{
},
"configurations":[
{
"classification":"export",
"properties":{
"PYSPARK_PYTHON":"python34"
},
"configurations":[
]
}
]
},
{
"classification":"spark",
"properties":{
"maximizeResourceAllocation":"true"
},
"configurations":[
]
}
]
根据我的阅读,设置maximizeResourceAllocation
属性应该告诉EMR配置Spark以充分利用群集上可用的所有资源。即,我应该有~75GB的内存......那么为什么我会得到~10.5GB的内存限制错误?
这是我正在运行的代码:
def sessionize(raw_data, timeout):
# https://www.dataiku.com/learn/guide/code/reshaping_data/sessionization.html
window = (pyspark.sql.Window.partitionBy("user_id", "site_id")
.orderBy("timestamp"))
diff = (pyspark.sql.functions.lag(raw_data.timestamp, 1)
.over(window))
time_diff = (raw_data.withColumn("time_diff", raw_data.timestamp - diff)
.withColumn("new_session", pyspark.sql.functions.when(pyspark.sql.functions.col("time_diff") >= timeout.seconds, 1).otherwise(0)))
window = (pyspark.sql.Window.partitionBy("user_id", "site_id")
.orderBy("timestamp")
.rowsBetween(-1, 0))
sessions = (time_diff.withColumn("session_id", pyspark.sql.functions.concat_ws("_", "user_id", "site_id", pyspark.sql.functions.sum("new_session").over(window))))
return sessions
def aggregate_sessions(sessions):
median = pyspark.sql.functions.udf(lambda x: statistics.median(x))
aggregated = sessions.groupBy(pyspark.sql.functions.col("session_id")).agg(
pyspark.sql.functions.first("site_id").alias("site_id"),
pyspark.sql.functions.first("user_id").alias("user_id"),
pyspark.sql.functions.count("id").alias("hits"),
pyspark.sql.functions.min("timestamp").alias("start"),
pyspark.sql.functions.max("timestamp").alias("finish"),
median(pyspark.sql.functions.collect_list("foo")).alias("foo"),
)
return aggregated
spark_context = pyspark.SparkContext(appName="process-raw-data")
spark_session = pyspark.sql.SparkSession(spark_context)
raw_data = spark_session.read.csv(sys.argv[1],
header=True,
inferSchema=True)
# Windowing doesn't seem to play nicely with TimestampTypes.
#
# Should be able to do this within the ``spark.read.csv`` call, I'd
# think. Need to look into it.
convert_to_unix = pyspark.sql.functions.udf(lambda s: arrow.get(s).timestamp)
raw_data = raw_data.withColumn("timestamp",
convert_to_unix(pyspark.sql.functions.col("timestamp")))
sessions = sessionize(raw_data, SESSION_TIMEOUT)
aggregated = aggregate_sessions(sessions)
aggregated.foreach(save_session)
基本上,只不过是窗口和groupBy来聚合数据。
从一些错误开始,并停止增加相同错误的数量。
我尝试使用 - conf spark.yarn.executor.memoryOverhead 运行spark-submit,但这似乎也无法解决问题。
答案 0 :(得分:41)
我感到你的痛苦......
我们遇到了与YARN上的Spark耗尽内存的类似问题。我们有五个64GB,16个核心虚拟机,无论我们设置spark.yarn.executor.memoryOverhead
的是什么,我们都无法为这些任务获得足够的内存 - 无论我们给它们多少内存,它们最终都会死掉。这是一个相对直接的Spark应用程序,导致这种情况发生。
我们发现虚拟机上的物理内存使用率非常低,但虚拟内存使用率非常高(尽管日志抱怨物理内存)。我们将yarn.nodemanager.vmem-check-enabled
中的yarn-site.xml
设置为false
,我们的容器不再被杀死,应用程序似乎按预期工作。
做更多的研究,我找到了为什么会发生这种情况的答案:https://www.mapr.com/blog/best-practices-yarn-resource-management
由于操作系统行为导致Centos / RHEL 6上存在大量虚拟内存分配,因此应禁用虚拟内存检查程序或将yarn.nodemanager.vmem-pmem-ratio增加到相对较大的值。
该页面包含指向IBM非常有用的页面的链接:https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
总之,glibc> 2.10改变了内存分配。虽然分配的大量虚拟内存不是世界末日,但它并不适用于YARN的默认设置。
您可以将yarn.nodemanager.vmem-check-enabled
环境变量设置为MALLOC_ARENA_MAX
中的较低数字,而不是将hadoop-env.sh
设置为false。
我建议阅读这两个页面 - 信息非常方便。
答案 1 :(得分:12)
如果您未使用spark-submit
,并且您正在寻找另一种方式来指定Duff提到的yarn.nodemanager.vmem-check-enabled
参数,则还有以下两种方式:
如果您正在使用JSON配置文件(传递给AWS CLI或boto3脚本),则必须添加以下配置:
[{
"Classification": "yarn-site",
"Properties": {
"yarn.nodemanager.vmem-check-enabled": "false"
}
}]
如果您使用EMR控制台,请添加以下配置:
classification=yarn-site,properties=[yarn.nodemanager.vmem-check-enabled=false]
答案 2 :(得分:7)
请参阅,
我在一个我现在正在工作的庞大集群中遇到了同样的问题。向工作人员添加内存不会解决问题。有时在进程聚合中,spark将使用比它更多的内存,并且spark作业将开始使用堆外内存。
一个简单的例子是:
如果您有reduceByKey
所需的数据集,有时会在一个工作程序中聚集更多数据而不是其他数据集,如果此数据会占用一个工作程序的内存,则会收到该错误消息。
添加选项spark.yarn.executor.memoryOverhead
将帮助您设置50%的工作用内存(仅用于测试,看它是否有效,您可以通过更多测试添加更少)。
但您需要了解Spark如何与群集中的内存分配配合使用:
关于内存分配的一件好事,如果你没有在执行中使用缓存,你可以设置火花来使用该sotorage空间来处理执行,以避免部分出现OOM错误。正如您在spark的文档中看到的那样:
这种设计确保了几种理想的特性。首先,不使用缓存的应用程序可以使用整个空间执行,从而避免不必要的磁盘溢出。其次,使用缓存的应用程序可以保留最小存储空间(R),其中数据块不受驱逐。最后,这种方法为各种工作负载提供了合理的开箱即用性能,而无需用户专业知识如何在内部划分内存。
但是我们怎么能用呢?
您可以更改某些配置,将MemoryOverhead
配置添加到作业通话中,但请考虑添加此配置:spark.memory.fraction
更改为0.8或0.85并将spark.memory.storageFraction
降低为0.35或0.2
其他配置可以提供帮助,但需要检查您的情况。完成所有这些配置here。
现在,在我的案例中有什么帮助。
我有一个拥有2.5K工作者和2.5TB RAM的集群。而且我们面临着像你这样的OOM错误。我们只需将spark.yarn.executor.memoryOverhead
增加到2048.我们启用dynamic allocation。当我们调用这个工作时,我们没有为工人设置内存,我们将其留给Spark来决定。我们只是设置了开销。
但是对于我的小集群的一些测试,改变了执行和存储内存的大小。这解决了这个问题。
答案 3 :(得分:2)
尝试重新分区。在我的情况下有效。
数据帧刚开始加载write.csv()
时并没有那么大。数据文件总计为10 MB左右,对于执行程序中的每个处理任务,可能需要说总共几个100 MB的内存。
我当时检查的分区数为2。
然后,在接下来的与其他表联接的操作中,它像滚雪球一样增长,添加了新列。然后我在某个步骤遇到了内存超出限制的问题。
我检查了分区的数量,它仍然是2,从我猜想的原始数据帧派生而来。
因此,我从一开始就尝试对其进行分区,然后再没有问题了。
我还没有阅读有关Spark和YARN的许多材料。我所知道的是节点中有执行程序。执行者可以根据资源处理许多任务。我的猜测是一个分区将被原子映射到一项任务。其数量决定了资源的使用。如果一个分区太大,Spark将无法对其进行切片。
一种合理的策略是首先确定节点和容器的内存(10GB或5GB)。理想情况下,两者都可以在任何时间处理数据服务。在给定5GB内存设置的情况下,您找到的一个分区的合理行,例如经过测试的1000行(在处理过程中不会失败任何步骤),我们可以使用以下伪代码来完成此操作:
RWS_PER_PARTITION = 1000
input_df = spark.write.csv("file_uri", *other_args)
total_rows = input_df.count()
original_num_partitions = input_df.getNumPartitions()
numPartitions = max(total_rows/RWS_PER_PARTITION, original_num_partitions)
input_df = input_df.repartition(numPartitions)
希望有帮助!
答案 4 :(得分:0)
在Spark 2.3.1上运行相对较小的作业的小型集群上,我遇到了相同的问题。 作业读取实木复合地板文件,使用groupBy / agg / first删除重复项,然后排序并写入新的实木复合地板。它在4个节点(4个vcore,32Gb RAM)上处理了51 GB的实木复合地板文件。
作业在聚合阶段一直失败。我写了bash脚本监视执行程序的内存使用情况,发现在阶段的中间,一位随机执行程序开始占用双倍内存,持续了几秒钟。当我将此时刻与GC日志相关联时,它与会耗尽大量内存的完整GC相匹配。
最后,我了解到该问题与GC有关。 ParallelGC和G1不断导致此问题,但是ConcMarkSweepGC改善了这种情况。仅在少量分区时出现此问题。我在安装了OpenJDK 64-Bit (build 25.171-b10)
的EMR上运行了作业。我不知道问题的根本原因,可能与JVM或操作系统有关。但这绝对与堆或堆外使用无关。
UPDATE1
尝试过Oracle HotSpot,此问题已重现。