根据现有列中的值将Spark DataFrame划分为选定数量的分区

时间:2019-02-11 14:21:14

标签: apache-spark apache-spark-sql grouping partitioning

我想在写入文件之前根据索引列将Spark DataFrame划分为偶数个分区。我想根据DataFrame的大小来控制要创建多少个分区,然后在使用partitionBy写入Parquet文件时使用then。

有一个示例DataFrame:

 i     b
 0    11
 1     9
 2    13
 3     2
 4    15
 5     3
 6    14
 7    16
 8    11
 9     9
 10   17
 11   10

假设我想基于i列中的值创建4个分区,则这些分区将对应于分配给g列的值:

g    i     b
0    0    11
0    1     9
0    2    13
1    3     2
1    4    15
1    5     3
2    6    14
2    7    16
2    8    11
3    9     9
3   10    17
3   11    10

在Spark中执行此操作的首选方式是什么?

1 个答案:

答案 0 :(得分:1)

尽管文档似乎有点难以理解,并且对这个问题做出了一些假设-例如,它希望将4个或N个file(?)作为输出,并以递增方式对列为“ i”的id进行声明,因此我自己的 Spark 2.4 改编示例,它获取20条记录并将它们分成4个均匀分布的分区,然后将其写出。我们去吧

val list = sc.makeRDD((1 to 20)).map((_, 1,"2019-01-01", "2019-01-01",1,2,"XXXXXXXXXXXXXXXXXXXXXXXXXX"))

val df = list.toDF("customer_id", "dummy", "report_date", "date", "value_1", "value_2", "dummy_string")
df.show(false)

仅显示一些条目:

+-----------+-----+-----------+----------+-------+-------+--------------------------+
|customer_id|dummy|report_date|date      |value_1|value_2|dummy_string              |
+-----------+-----+-----------+----------+-------+-------+--------------------------+
|1          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
|2          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
|3          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
|4          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
|5          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
|6          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
|7          |1    |2019-01-01 |2019-01-01|1      |2      |XXXXXXXXXXXXXXXXXXXXXXXXXX|
...

然后-包括一些额外的排序方法-不需要,可以使用所有格式:

df.repartitionByRange(4, $"customer_id")
  .sortWithinPartitions("customer_id", "date", "value_1")
  .write
  .parquet("/tmp/SOQ6")

如下图所示,它提供了4个文件:

enter image description here

您可以看到4个文件,第一部分和最后部分的命名是显而易见的。正在运行:

val lines = spark.read.parquet("/tmp/SOQ6/part-00000-tid-2518447510905190948-a81455f6-6c0b-4e02-89b0-57dfddf1fb97-1200-c000.snappy.parquet")
val words = lines.collect
lines.count

显示5条记录,以及根据数据帧连续排序的内容。

lines: org.apache.spark.sql.DataFrame = [customer_id: int, dummy: int ... 5 more fields]
 words: Array[org.apache.spark.sql.Row] = Array([1,1,2019-01-01,2019-01-01,1,2,XXXXXXXXXXXXXXXXXXXXXXXXXX], [2,1,2019-01-01,2019-01-01,1,2,XXXXXXXXXXXXXXXXXXXXXXXXXX], [3,1,2019-01-01,2019-01-01,1,2,XXXXXXXXXXXXXXXXXXXXXXXXXX], [4,1,2019-01-01,2019-01-01,1,2,XXXXXXXXXXXXXXXXXXXXXXXXXX], [5,1,2019-01-01,2019-01-01,1,2,XXXXXXXXXXXXXXXXXXXXXXXXXX])
res11: Long = 5

在所有文件上都使用此选项,但只显示一个。

  

最终评论

     

这是一个好主意是一个不同的故事,例如想起非广播的JOIN就是一个问题。

     

此外,我显然不会对4进行硬编码,而是为N应用一些公式以应用于partitionByRange!例如:

val N = some calculation based on counts in DF and your cluster 
val df2 = df.repartition(N, $"c1", $"c2")
     

由于文档尚不完全清楚,您必须测试DF Writer。

     

在具有2M条记录的EMR群集上进行检查,并且在输出方面也检查了4个文件。