我有一个包含很少bigint或string列的hive表,超过3800万行,总大小略超过1GB,测试环境是一个小型独立集群,有4个工作节点,每个都有8GB内存,Spark 1.4。 从Spark-sql shell开始,我尝试执行SQL
create table bla as select user_id, brand_id, sum(cnt) from foo group by user_id, brand_id
几次,工作总是陷入第一阶段,几乎没有任务挂起。
GC报告似乎表明没有足够的内存来存储瞬态对象,并且进程正在等待完整的GC完成。
一个挂起节点的GC输出:
4572.821:[GC [PSYoungGen:1707488K-> 595392K(1963136K)] 5871286K-> 5321918K(7555584K),1.2474880 secs] [时间:用户= 16.32 sys = 0.43,实际= 1.25秒] 4575.891:[GC [PSYoungGen:1739840K-> 559488K(1970304K)] 6466366K-> 5882382K(7562752K),0.9950000 secs] [次:用户= 12.69 sys = 0.72,real = 1.00 secs] 4576.886:[完整GC
我的问题是:我的sql是否真的需要超过32GB的内存来运行1GB的数据?或者是否有我错过的快速优化设置? 我可以用较少的记录成功执行sql, 或者甚至使用完整数据,如果我在查询中避免使用sum / avg,则查询
select user_id, brand_id, count(cnt)
或
select user_id, brand_id, concat(xx)
所有工作和每个节点上的内存占用量在GC之后不会超过1或2GB。
第二个问题:初始阶段总是使用10个分区,如何改变并行性?
由于 万春
答案 0 :(得分:0)
针对您的具体问题:
Spark需要多少内存取决于您的数据以及您对其执行的操作。在您的特定情况下,唯一GROUP BY
聚合越大,内存开销就越大。还要注意,JVM数据结构通常非常低效。有关详细信息,请参阅this presentation。最后但并非最不重要的是,您必须小心比较尺寸。表1Gb是否在磁盘上?它是否使用自动压缩的优化格式,如Parquet?
有两种方法可以改变并行性。首先,您可以更改输入文件的数量。其次,您可以使用repartition(numPartitions)
显式重新分区数据。在RDD doc。
您可以根据现有分区的数量以编程方式重新分区,以达到所需的并行级别或数据文件粒度,例如
val df = ctx.jsonFile("file://...")
df.repartition(10 * df.rdd.partitions.size)
通过使用df.rdd.partitions.size
,您还可以根据群集大小动态调整分区数,例如,获取分区数,例如,执行程序CPU数量的4倍等。
如果您遇到内存问题,增加分区数量是查找在群集约束范围内工作的处理管道的良好起点。
还有其他与内存相关的优化:
如果在缩减阶段内存不足,您可能需要增加spark.shuffle.memoryFraction
如果您的操作无法从缓存中受益,则可以减少spark.storage.memoryFraction
。
有关configuring Spark的更多信息。
答案 1 :(得分:0)
只想提供更新,我真的相信当前的spark发布在其某些聚合函数上有一个次优的实现。例如如果我只是在查询中用count()或max()替换sum(),那么查询会在30-40秒内成功返回。 最终我使sum()查询通过双倍工作来工作,因此在3分钟内完成作业的内存的两倍。 出于对Tungsten的好奇心,我从源头构建了spark1.5,在spark1.4中启动了相同的查询,花了3分钟,完成时间是40到55秒的范围。 干杯, 万春