我正在一个相当小的数据集上做一个简单的组(HDFS中的80个文件,总共几个演出)。我在一个纱线集群中的8台低内存机器上运行Spark,即:
spark-submit ... --master yarn-client --num-executors 8 --executor-memory 3000m --executor-cores 1
数据集由长度为500-2000的字符串组成。
我正在尝试做一个简单的groupByKey
(见下文),但它失败并出现java.lang.OutOfMemoryError: GC overhead limit exceeded
异常
val keyvals = sc.newAPIHadoopFile("hdfs://...")
.map( someobj.produceKeyValTuple )
keyvals.groupByKey().count()
我可以毫无问题地使用reduceByKey
计算群组大小,确保自己问题不是由一个过大的群体造成的,也不是由过多的群体造成的:
keyvals.map(s => (s._1, 1)).reduceByKey((a,b) => a+b).collect().foreach(println)
// produces:
// (key1,139368)
// (key2,35335)
// (key3,392744)
// ...
// (key13,197941)
我尝试过重新格式化,改组并增加groupBy并行度:
keyvals.groupByKey(24).count // fails
keyvals.groupByKey(3000).count // fails
keyvals.coalesce(24, true).groupByKey(24).count // fails
keyvals.coalesce(3000, true).groupByKey(3000).count // fails
keyvals.coalesce(24, false).groupByKey(24).count // fails
keyvals.coalesce(3000, false).groupByKey(3000).count // fails
我尝试使用spark.default.parallelism
,并将spark.shuffle.memoryFraction
增加到0.8
,同时将spark.storage.memoryFraction
降低为0.1
失败阶段(计数)将在3000的任务2999上失败。
我似乎无法找到任何暗示groupBy不应该只溢出到磁盘而不是将内容保存在内存中的东西,但我无法让它正常工作,即使在相当小的数据集上也是如此。这应该不是这样的,我一定做错了,但我不知道从哪里开始调试这个!
答案 0 :(得分:7)
Patrick Wendell阐述了groupBy运算符on the mailing list的细节。外卖信息如下:
在分区内,事情会溢出[...]此溢出只能在跨键时发生。目前在密钥内不会发生溢出。 [...]在GroupBy的一个密钥内溢出可能会在下一个Spark,Spark 1.2版本中结束。 [...]如果目标实际上只是将与每个组关联的所有值写入磁盘,并且与单个组关联的值大于适合内存,则现在无法使用groupBy运算符完成此操作。
他进一步建议解决方法:
解决此问题的最佳方法取决于您尝试对下游数据执行的操作。通常,方法涉及对任何非常大的组进行细分,例如,将小范围(1-10)中的散列值附加到大键。然后,您的下游代码必须处理每个组的聚合部分值。如果您的目标只是在一个大文件上按顺序将每个组放在磁盘上,那么您也可以使用散列后缀调用
sortByKey
。排序函数在Spark 1.1(已预发布)中外部化。