我需要按6列对数据集数据进行分区:区域/年/月/日/天/ id /四键 在顶层,我只有二进制区域状态,而在底层,实际上是进入多个分区的地方。 假设我们有2个区域/通常是1年/通常是1个月/ 3-4天/ 100-150个ID / 50-200个四键 当我执行此操作时,我会得到真正不平衡的随机播放操作,有时执行器由于超出内存限制而失败。 我还从History UI中注意到,在帽子阶段的某些任务非常大(〜15Gb),而其他任务则很小(〜1Gb)。
我尝试过玩
sqlContext.setConf("spark.sql.shuffle.partitions", "3000")
我也试图扩展执行程序的数量,但是使用相同的内存设置。那我得到的错误:
19/04/10 09:47:36 INFO ApplicationMaster: Final app status: FAILED, exitCode: 15, (reason: User class threw exception: org.apache.spark.SparkException: Job aborted.
at org.apache.spark.sql.execution.datasources.FileFormatWriter$.write(FileFormatWriter.scala:196)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.run(InsertIntoHadoopFsRelationCommand.scala:159)
at org.apache.spark.sql.execution.command.DataWritingCommandExec.sideEffectResult$lzycompute(commands.scala:104)
at org.apache.spark.sql.execution.command.DataWritingCommandExec.sideEffectResult(commands.scala:102)
at org.apache.spark.sql.execution.command.DataWritingCommandExec.doExecute(commands.scala:122)
at org.apache.spark.sql.execution.SparkPlan$$anonfun$execute$1.apply(SparkPlan.scala:131)
at org.apache.spark.sql.execution.SparkPlan$$anonfun$execute$1.apply(SparkPlan.scala:127)
at org.apache.spark.sql.execution.SparkPlan$$anonfun$executeQuery$1.apply(SparkPlan.scala:155)
at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:151)
at org.apache.spark.sql.execution.SparkPlan.executeQuery(SparkPlan.scala:152)
at org.apache.spark.sql.execution.SparkPlan.execute(SparkPlan.scala:127)
at org.apache.spark.sql.execution.QueryExecution.toRdd$lzycompute(QueryExecution.scala:80)
at org.apache.spark.sql.execution.QueryExecution.toRdd(QueryExecution.scala:80)
at org.apache.spark.sql.DataFrameWriter$$anonfun$runCommand$1.apply(DataFrameWriter.scala:668)
at org.apache.spark.sql.DataFrameWriter$$anonfun$runCommand$1.apply(DataFrameWriter.scala:668)
at org.apache.spark.sql.execution.SQLExecution$$anonfun$withNewExecutionId$1.apply(SQLExecution.scala:78)
at org.apache.spark.sql.execution.SQLExecution$.withSQLConfPropagated(SQLExecution.scala:125)
at org.apache.spark.sql.execution.SQLExecution$.withNewExecutionId(SQLExecution.scala:73)
at org.apache.spark.sql.DataFrameWriter.runCommand(DataFrameWriter.scala:668)
at org.apache.spark.sql.DataFrameWriter.saveToV1Source(DataFrameWriter.scala:276)
at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:270)
at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:228)
...
// stage: DataFrame
val partitionColumns = List("region", "year", "month", "day", "id", "quadkey")
stage.repartition(partitionColumns.map(new org.apache.spark.sql.Column(_)):_*)
.write.partitionBy(partitionColumns:_*)
.format("parquet")
.option("compression", "gzip")
.mode(SaveMode.Append)
.save(destUrl)
我期望在保存阶段完成平衡的任务,为此应该设置哪些随机设置?还是我必须拥有超过20-25 Gb内存的执行器?在这种情况下应采用什么缩放方法?
答案 0 :(得分:1)
一种方法可能是向repartition
添加更多列,并使该列具有高基数(记录ID或一些随机值)
如果文件数量变大,则尝试设置numPartitions
,然后对列进行分区。
df.repartition(numPartitions, partition_cols_including_high_cardinality_column:_*).write........
================================================ =========================== 编辑:
在某些情况下,数据偏向某些分区组合的数据要多于其他分区组合,那么用同一列重新分区可能不是一个好主意。
在重新分区中,所有与分区键组合匹配的数据都将首先在同一执行程序上收集,如果您的partitionBy和repartition具有相同的列参数,则将生成一个文件。因此,在这种情况下,几乎没有分区组合将具有〜15Gb之类的文件,而具有〜1Gb之类的文件则对HDFS这样的数据源而言并不理想
所以我在这里建议的是要有重新分配列,以便在执行程序上平均分配数据。考虑到这一点,我们已经在某些列组合E上对数据进行了重新分区,它为每个执行者产生了400行数据,然后每个执行者将根据partitionBy规范写入其数据。当您检查最终输出时,每个分区的文件数将等于接收具有相同partitionBy规范的行的执行程序的数量。执行程序的数量由分区列规范决定。
我上面建议的是要有不同的列集来进行分区,这将有助于在执行程序上平均分配数据。如果由于某种原因无法在数据上添加,请添加一些随机列(称为salting
的技术)。添加numPartitions
的选项可固定处理数据的执行程序的上限,从而固定写入分区目录的文件数。当您的重新分区列具有高基数时,设置numPartitions非常有用,因为这会在输出目录中创建许多文件。
import org.apache.spark.sql.functions.rand
df.repartition(numPartitions, $"some_col_1", rand)
.write.partitionBy("some_col")
.parquet("partitioned_lake")
在这里,通过修复numPartitions,我们可以确保每个partitionBy规范的输出将最多包含numPartitions个文件。
有用的链接-http://tantusdata.com/spark-shuffle-case-2-repartitioning-skewed-data/
希望这会有所帮助