web-dev-qa-db-fra.com

Comment définir le partitionnement de DataFrame?

J'ai commencé à utiliser Spark SQL et DataFrames dans Spark 1.4.0. Je veux définir un partitionneur personnalisé sur les DataFrames, dans Scala, mais je ne vois pas comment faire cela.

L'une des tables de données avec laquelle je travaille contient une liste des transactions, par compte, silimar à l'exemple suivant.

Account   Date       Type       Amount
1001    2014-04-01  Purchase    100.00
1001    2014-04-01  Purchase     50.00
1001    2014-04-05  Purchase     70.00
1001    2014-04-01  Payment    -150.00
1002    2014-04-01  Purchase     80.00
1002    2014-04-02  Purchase     22.00
1002    2014-04-04  Payment    -120.00
1002    2014-04-04  Purchase     60.00
1003    2014-04-02  Purchase    210.00
1003    2014-04-03  Purchase     15.00

Au moins au début, la plupart des calculs seront effectués entre les transactions d’un compte. Je souhaite donc que les données soient partitionnées de sorte que toutes les transactions d'un compte se trouvent dans la même partition Spark.

Mais je ne vois pas un moyen de définir cela. La classe DataFrame a une méthode appelée 'repartition (Int)', dans laquelle vous pouvez spécifier le nombre de partitions à créer. Mais je ne vois aucune méthode disponible pour définir un partitionneur personnalisé pour un DataFrame, telle que celle pouvant être spécifiée pour un RDD.

Les données source sont stockées dans Parquet. J'ai constaté que, lors de l'écriture d'un DataFrame dans Parquet, vous pouvez spécifier une colonne à partitionner. Par conséquent, je pourrais probablement dire à Parquet de partitionner ses données à l'aide de la colonne "Compte". Mais il pourrait y avoir des millions de comptes, et si je comprends bien Parquet, cela créerait un répertoire distinct pour chaque compte, de sorte que cela ne semblait pas être une solution raisonnable.

Existe-t-il un moyen d’obtenir que Spark partitionne ce DataFrame afin que toutes les données d’un compte se trouvent dans la même partition?

115
rake

Dans Spark <1.6 Si vous créez une HiveContext et non l'ancien un_ SqlContext, vous pouvez utiliser le HiveQLDISTRIBUTE BY colX... (garantit à chacun N réducteurs obtient des plages non superposées de x) & CLUSTER BY colX... (raccourci pour Distribuer par et Trier par) par exemple;

df.registerTempTable("partitionMe")
hiveCtx.sql("select * from partitionMe DISTRIBUTE BY accountId SORT BY accountId, date")

Vous ne savez pas comment cela s’intègre avec Spark DF api. Ces mots-clés ne sont pas pris en charge dans le SqlContext normal (notez que vous n'avez pas besoin d'un méta-magasin Hive pour utiliser le HiveContext).

EDIT: Spark 1.6+ l’a désormais dans l’API native DataFrame

11
NightWolf

Utilisez le DataFrame renvoyé par:

yourDF.orderBy(account)

Il n'y a pas de moyen explicite d'utiliser partitionBy sur un DataFrame, uniquement sur un PairRDD, mais lorsque vous triez un DataFrame, il l'utilisera dans son LogicalPlan et cela vous aidera lorsque vous devrez effectuer des calculs sur chaque compte.

Je suis juste tombé sur le même problème, avec un cadre de données que je veux partitionner par compte. Je suppose que lorsque vous dites "voulez que les données soient partitionnées de manière à ce que toutes les transactions d'un compte se trouvent dans la même partition Spark", vous le voulez pour l'échelle et les performances, mais votre code ne dépendre de cela (comme utiliser mapPartitions() etc), non?

7
Romi Kuntsman

J'ai pu faire cela en utilisant RDD. Mais je ne sais pas si c'est une solution acceptable pour vous. Une fois que vous avez le DF disponible en tant que RDD, vous pouvez appliquer repartitionAndSortWithinPartitions pour effectuer un repartitionnement personnalisé des données.

Voici un exemple que j'ai utilisé:

class DatePartitioner(partitions: Int) extends Partitioner {

  override def getPartition(key: Any): Int = {
    val start_time: Long = key.asInstanceOf[Long]
    Objects.hash(Array(start_time)) % partitions
  }

  override def numPartitions: Int = partitions
}

myRDD
  .repartitionAndSortWithinPartitions(new DatePartitioner(24))
  .map { v => v._2 }
  .toDF()
  .write.mode(SaveMode.Overwrite)
5
Developer

Donc, pour commencer avec une sorte de réponse:) - Vous ne pouvez pas

Je ne suis pas un expert, mais si je comprends bien, les DataFrames ne correspondent pas à rdd et DataFrame n'a pas de partitionnement.

Généralement, l’idée de DataFrame est de fournir un autre niveau d’abstraction permettant de gérer ces problèmes lui-même. Les requêtes sur DataFrame sont converties en un plan logique qui est ensuite traduit en opérations sur des RDD. Le partitionnement que vous avez suggéré sera probablement appliqué automatiquement ou du moins devrait l'être.

Si vous ne croyez pas que SparkSQL fournira une sorte de travail optimal, vous pouvez toujours transformer DataFrame en RDD [Row] comme suggéré dans les commentaires.

5
Dawid Wysakowicz