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?
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
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?
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)
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.