我在hive中有一个100 GB大小的表。我试图分组,计数功能和存储结果作为蜂巢表...我的硬盘有600 GB的空间,到时间工作达到70%所有的磁盘空间都被占用。
所以我的工作失败了...我怎样才能最小化shuffle数据写入
hiveCtx.sql("select * from gm.final_orc")
.repartition(300)
.groupBy('col1, 'col2).count
.orderBy('count desc)
.write.saveAsTable("gm.result")
答案 0 :(得分:0)
在基于云的执行环境中,添加更多磁盘通常是一种非常简单且便宜的选择。如果您的环境不允许这样,并且您已经验证了shuffles settings是合理的,例如,压缩(默认情况下已启用)未更改,那么只有一个解决方案:实施您自己的分阶段map-reduce使用可以通过sum
重新汇总计数的事实。
col1
和col2
执行计数,作为单独的Spark操作。为简单起见,我们假设col1
是一个整数。以下是我如何将处理分解为8个单独的作业,重新聚合其输出。如果col1
不是整数,您可以对其进行哈希处理,也可以使用其他列。
def splitTableName(i: Int) = s"tmp.gm.result.part-$i"
// Source data
val df = hiveCtx.sql("select col1, col2 from gm.final_orc")
// Number of splits
val splits = 8
// Materialize partial aggregations
val tables = for {
i <- 0 until splits
tableName = splitTableName(i)
// If col1 % splits will create very skewed data, hash it first, e.g.,
// hash(col1) % splits. hash() uses Murmur3.
_ = df.filter('col1 % splits === i)
// repartition only if you need to, e.g., massive partitions are causing OOM
// better to increase the number of splits and/or hash to un-skew skewed data
.groupBy('col1, 'col2).count
.write.saveAsTable(tableName)
} yield hiveCtx.table(tableName)
// Final aggregation
tables.reduce(_ union _)
.groupBy('col1, 'col2)
.agg(sum('count).as("count"))
.orderBy('count.desc)
.write.saveAsTable("gm.result")
// Cleanup temporary tables
(0 until splits).foreach { i =>
hiveCtx.sql(s"drop table ${splitTableName(i)}")
}
如果col1
和col2
如此多样化和/或大到部分聚合存储导致磁盘空间问题,那么您必须考虑以下其中一项:
较小的分割数通常会占用较少的磁盘空间。
对col1
进行排序会有所帮助(因为Parquet运行长度编码),但这会降低执行速度。
如何创建独立的拆分,例如,查找col1
的不同值,将其划分为组。
如果您的磁盘空间极短,则必须实施多步重新聚合。最简单的方法是一次生成一个拆分并保持运行的聚合。执行速度会慢很多,但会占用更少的磁盘空间。
希望这有帮助!