web-dev-qa-db-fra.com

Spark connaît-il la clé de partitionnement d'un DataFrame?

Je veux savoir si Spark connaît la clé de partitionnement du fichier parquet et utilise ces informations pour éviter les brassages.

Contexte:

Exécution Spark 2.0.1 exécutant SparkSession local. J'ai un jeu de données csv que j'enregistre en tant que fichier parquet sur mon disque comme ceci:

val df0 = spark
  .read
  .format("csv")
  .option("header", true)
  .option("delimiter", ";")
  .option("inferSchema", false)
  .load("SomeFile.csv"))


val df = df0.repartition(partitionExprs = col("numerocarte"), numPartitions = 42)

df.write
  .mode(SaveMode.Overwrite)
  .format("parquet")
  .option("inferSchema", false)
  .save("SomeFile.parquet")

Je crée 42 partitions par colonne numerocarte. Cela devrait regrouper plusieurs numerocarte dans la même partition. Je ne veux pas faire partitionBy ("numerocarte") au moment write parce que je ne veux pas une partition par carte. Ce serait des millions d'entre eux.

Après cela, dans un autre script, j'ai lu ce SomeFile.parquet fichier parquet et faire quelques opérations dessus. En particulier, je gère un window function sur celui-ci où le partitionnement se fait sur la même colonne que le fichier parquet a été repartitionné.

import org.Apache.spark.sql.expressions.Window
import org.Apache.spark.sql.functions._

val df2 = spark.read
  .format("parquet")
  .option("header", true)
  .option("inferSchema", false)
  .load("SomeFile.parquet")

val w = Window.partitionBy(col("numerocarte"))
.orderBy(col("SomeColumn"))

df2.withColumn("NewColumnName",
      sum(col("dollars").over(w))

Après read je peux voir que le repartition a fonctionné comme prévu et DataFrame df2 a 42 partitions et dans chacune d'elles se trouvent des cartes différentes.

Questions:

  1. Spark sait-il que la trame de données df2 est partitionné par la colonne numerocarte?
  2. S'il le sait, la fonction de fenêtre ne sera pas mélangée. Vrai?
  3. S'il ne sait pas, il fera un shuffle dans la fonction de fenêtre. Vrai?
  4. S'il ne le sait pas, comment puis-je dire Spark les données sont déjà partitionnées par la colonne de droite?
  5. Comment puis-je vérifier une clé de partitionnement de DataFrame? Existe-t-il une commande pour cela? Je sais comment vérifier le nombre de partitions mais comment voir la clé de partitionnement?
  6. Lorsque j'imprime le nombre de partitions dans un fichier après chaque étape, j'ai 42 partitions après read et 200 partitions après withColumn ce qui suggère que Spark repartitionné mon DataFrame.
  7. Si j'ai deux tables différentes repartitionnées avec la même colonne, la jointure utiliserait-elle ces informations?
13
astro_asz

Spark sait-il que la trame de données df2 est partitionnée par une colonne de chiffres?

Ce ne est pas.

S'il ne sait pas, comment puis-je dire Spark les données sont déjà partitionnées par la colonne de droite?

Non. Ce n'est pas parce que vous enregistrez des données qui ont été mélangées qu'elles seront chargées avec les mêmes divisions.

Comment puis-je vérifier une clé de partitionnement de DataFrame?

Il n'y a pas de clé de partitionnement une fois que vous avez chargé les données, mais vous pouvez vérifier queryExecution pour Partitioner.


En pratique:

  • Si vous souhaitez prendre en charge des pushdowns efficaces sur la clé, utilisez la méthode partitionBy de DataFrameWriter.
  • Si vous souhaitez une prise en charge limitée des optimisations de jointure, utilisez bucketBy avec les métastores et les tables persistantes.

Voir Comment définir le partitionnement de DataFrame? pour des exemples détaillés.

12
hi-zir

Je réponds à ma propre question pour référence future ce qui a fonctionné.

Suite à la suggestion de @ user8371915, bucketBy fonctionne!

J'enregistre mon DataFrame df:

df.write
  .bucketBy(250, "userid")
  .saveAsTable("myNewTable")

Puis quand j'ai besoin de charger ce tableau:

val df2 = spark.sql("SELECT * FROM myNewTable")

val w = Window.partitionBy("userid")

val df3 = df2.withColumn("newColumnName", sum(col("someColumn")).over(w)
df3.explain

Je confirme que lorsque je fais des fonctions de fenêtre sur df2 Partitionné par userid il n'y a pas de mélange! Merci @ user8371915!

Certaines choses que j'ai apprises en enquêtant

  • myNewTable ressemble à un fichier de parquet normal mais ce n'est pas le cas. Vous pouvez le lire normalement avec spark.read.format("parquet").load("path/to/myNewTable") mais le DataFrame créé de cette façon ne conservera pas le partitionnement d'origine! Vous devez utiliser spark.sqlselect pour obtenir une partition correctement DataFrame.
  • Vous pouvez regarder à l'intérieur du tableau avec spark.sql("describe formatted myNewTable").collect.foreach(println). Cela vous indiquera quelles colonnes ont été utilisées pour le regroupement et le nombre de regroupements.
  • Les fonctions et jointures de fenêtre qui tirent parti du partitionnement nécessitent souvent un tri. Vous pouvez trier les données de vos compartiments au moment de l'écriture à l'aide de .sortBy() et le tri sera également conservé dans la table Hive. df.write.bucketBy(250, "userid").sortBy("somColumnName").saveAsTable("myNewTable")
  • Lorsque vous travaillez en mode local, la table myNewTable est enregistrée dans un dossier spark-warehouse Dans mon projet local Scala SBT. Lors de l'enregistrement en mode cluster avec mesos via spark-submit, Il est enregistré dans l'entrepôt Hive. Pour moi, il était situé dans /user/Hive/warehouse.
  • Lorsque vous faites spark-submit, Vous devez ajouter à votre SparkSession deux options: .config("Hive.metastore.uris", "thrift://addres-to-your-master:9083") et .enableHiveSupport(). Sinon, les tables Hive que vous avez créées ne seront pas visibles.
  • Si vous souhaitez enregistrer votre table dans une base de données spécifique, effectuez spark.sql("USE your database") avant le regroupement.

Mise à jour 05-02-2018

J'ai rencontré des problèmes avec spark bucketing et création de tables Hive. Veuillez vous référer aux questions, réponses et commentaires dans Pourquoi est Spark saveAsTable avec bucketBy) créer des milliers de fichiers?

8
astro_asz