Apache spark 按列进行Spark重新分区,每列具有动态分区数
如何根据列中的项数对数据帧进行分区。假设我们有一个100人的数据框(列是Apache spark 按列进行Spark重新分区,每列具有动态分区数,apache-spark,Apache Spark,如何根据列中的项数对数据帧进行分区。假设我们有一个100人的数据框(列是名字和国家),我们想为一个国家的每10个人创建一个分区 如果我们的数据集包含80个中国人、15个法国人和5个古巴人,那么我们需要8个中国分区、2个法国分区和1个古巴分区 以下是无法工作的代码: df.repartition($“country”):这将为中国创建一个分区,为法国创建一个分区,为古巴创建一个分区 df.repartition(8,$“country”,rand):这将为每个国家创建最多8个分区,因此应该为中国
名字和国家
),我们想为一个国家的每10个人创建一个分区
如果我们的数据集包含80个中国人、15个法国人和5个古巴人,那么我们需要8个中国分区、2个法国分区和1个古巴分区
以下是无法工作的代码:
df.repartition($“country”)
:这将为中国创建一个分区,为法国创建一个分区,为古巴创建一个分区
df.repartition(8,$“country”,rand)
:这将为每个国家创建最多8个分区,因此应该为中国创建8个分区,但法国和古巴分区未知。法国可以分为8个分区,古巴可以分为5个分区。有关更多详细信息,请参阅
以下是repartition()
文档:
当我查看repartition()
方法时,我甚至没有看到一个包含三个参数的方法,因此看起来有些行为没有记录在案
有没有办法动态设置每列的分区数?这将使创建分区数据集变得更加容易。由于spark分区数据的方式,您将无法准确地完成这一任务。Spark获取在重新分区中指定的列,将该值散列为64b长,然后按分区数对该值进行模化。这样,分区的数量是确定的。它以这种方式工作的原因是,除了确保哈希在连接的左侧和右侧相同外,连接还需要在连接的左侧和右侧匹配数量的分区
“我们希望为一个国家的每10个人创建一个分区。”
你到底想在这里完成什么?分区中只有10行可能会对性能造成严重影响。您是否正在尝试创建分区表,其中分区中的每个文件都被保证只有x行数
“df.重新分区($“国家”):这将为中国创建一个分区,为法国创建一个分区,为古巴创建一个分区”
这将实际创建一个数据帧,其中默认的随机分区数按国家散列
def repartition(partitionExprs: Column*): Dataset[T] = {
repartition(sparkSession.sessionState.conf.numShufflePartitions, partitionExprs: _*)
}
“df.repartition(8,$“country”,rand):这将为每个国家创建最多8个分区,因此它应该为中国创建8个分区,但法国和古巴分区未知。法国可以在8个分区中,古巴可以在5个分区中。有关详细信息,请参阅此答案。”
像怀斯一样,这是一个微妙的错误。在这8个分区中,只有8个分区的国家基本上是随机排列的。下面的代码将为每个数据文件创建10行():
以下是Spark 2.2之前的代码,它将为每个数据文件创建大约10行:
val desiredRowsPerPartition = 10
val joinedDF = df
.join(countDF, Seq("person_country"))
.withColumn(
"my_secret_partition_key",
(rand(10) * col("count") / desiredRowsPerPartition).cast(IntegerType)
)
val outputPath = new java.io.File("./tmp/partitioned_lake6/").getCanonicalPath
joinedDF
.repartition(col("person_country"), col("my_secret_partition_key"))
.drop("count", "my_secret_partition_key")
.write
.partitionBy("person_country")
.csv(outputPath)
关于第二次调用中的3个参数,$“country”,rand
一起作为partitionExprs
感谢您指出我的细微错误。对于10行,不需要此代码,但在倾斜的大型数据集上创建分区数据湖时,这非常重要。
val desiredRowsPerPartition = 10
val joinedDF = df
.join(countDF, Seq("person_country"))
.withColumn(
"my_secret_partition_key",
(rand(10) * col("count") / desiredRowsPerPartition).cast(IntegerType)
)
val outputPath = new java.io.File("./tmp/partitioned_lake6/").getCanonicalPath
joinedDF
.repartition(col("person_country"), col("my_secret_partition_key"))
.drop("count", "my_secret_partition_key")
.write
.partitionBy("person_country")
.csv(outputPath)