web-dev-qa-db-fra.com

L'utilisation de Spark pour écrire un fichier parquet sur s3 sur s3a est très lente

J'essaie d'écrire un fichier parquet dans Amazon S3 En utilisant Spark 1.6.1. Le petit parquet que je génère est ~2GB Une fois écrit donc ce ne sont pas autant de données. J'essaie de prouver Spark comme une plate-forme que je peux utiliser.

Fondamentalement, je vais mettre en place un star schema Avec dataframes, puis je vais écrire ces tables sur du parquet. Les données proviennent de fichiers csv fournis par un fournisseur et j'utilise Spark comme plate-forme ETL. J'ai actuellement un cluster à 3 nœuds dans ec2(r3.2xlarge) Donc 120GB De mémoire sur les exécuteurs et 16 cœurs au total.

Les fichiers d'entrée totalisent environ 22 Go et j'extraye environ 2 Go de ces données pour l'instant. Finalement, ce sera de nombreux téraoctets lorsque je commencerai à charger l'ensemble de données complet.

Voici mon étincelle/scala pseudocode:

  def loadStage(): Unit = {
    sc.hadoopConfiguration.set("fs.s3a.buffer.dir", "/tmp/tempData")
    sc.hadoopConfiguration.set("spark.sql.parquet.output.committer.class","org.Apache.spark.sql.parquet.DirectParquetOutputCommitter")
    sc.hadoopConfiguration.set("spark.sql.Hive.convertMetastoreParquet","false")
    var sqlCtx = new SQLContext(sc)


    val DataFile = sc.textFile("s3a://my-bucket/archive/*/file*.gz")

    //Setup header table/df
    val header_rec = DataFile.map(_.split("\\|")).filter(x=> x(0) == "1")
    val headerSchemaDef = "market_no,rel_date,field1, field2, field3....."
    val headerSchema = StructType(headerSchemaDef.split(",").map(fieldName => StructField(fieldName, StringType,false)))
    val headerRecords = header_rec.map(p => Row(p(3), p(8), p(1), p(2), p(4), p(5), p(6) ))
    val header = sqlCtx.createDataFrame(headerRecords, headerSchema)
    header.registerTempTable("header")
    sqlCtx.cacheTable("header")


    //Setup fact table/df
    val fact_recs = DataFile.map(_.split("\\|")).filter(x=> x(0) == "2")
    val factSchemaDef = "market_no,rel_date,field1, field2, field3....."
    val factSchema = StructType(factSchemaDef.split(",").map(fieldName => StructField(fieldName, StringType,false)))
    val records = fact_recs.map(p => Row(p(11), p(12), p(1), p(2), p(3), p(4), p(5), p(6), p(7), p(8), p(9), p(10)))
    val df = sqlCtx.createDataFrame(records, factSchema)
    df.registerTempTable("fact")

    val results = sqlCtx.sql("select fact.* from header inner join fact on fact.market_no = header.market_no and fact.rel_date = header.rel_date")


    println(results.count())



    results.coalesce(1).write.mode(SaveMode.Overwrite).parquet("s3a://my-bucket/a/joined_data.parquet")


  }

Le décompte prend environ 2 minutes pour 465884512 lignes. L'écriture sur le parquet prend 8 minutes

Je comprends que le coalesce fait un shuffle au pilote qui fait l'écriture .... mais le temps qu'il me faut me fait penser que je fais quelque chose de grave. Sans le coalesce, cela prend encore 15 minutes, ce qui est encore trop long à l'OMI et me donne une tonne de petits fichiers parquet. J'aimerais avoir un gros fichier par jour de données que j'aurai. J'ai du code pour faire le partitionnement par une valeur de champ aussi, et c'est tout aussi lent. J'ai également essayé de produire ceci sur csv et cela prend ~ 1 heure.

De plus, je ne configure pas vraiment les accessoires d'exécution lorsque je soumets mon travail. Les statistiques de ma console pour un travail sont les suivantes:

  • Travailleurs vivants: 2
  • Noyaux utilisés: 16 au total, 16 utilisés
  • Mémoire utilisée: 117,5 Go au total, 107,5 Go utilisés
  • Demandes: 1 en cours, 5 terminées
  • Pilotes: 0 en cours d'exécution, 0 terminés
  • Statut: VIVANT
17
Brutus35

Les valeurs par défaut de Spark provoquent une grande quantité (probablement) de surcharge inutile pendant les opérations d'E/S, en particulier lors de l'écriture sur S3. Cet article en parle plus en détail, mais il y a 2 paramètres que vous voudrez envisager de modifier.

  • Utilisation de DirectParquetOutputCommitter. Par défaut, Spark enregistrera toutes les données dans un dossier temporaire puis déplacera ces fichiers par la suite. L'utilisation de DirectParquetOutputCommitter fera gagner du temps en écrivant directement sur le chemin de sortie S3

    • N'est plus disponible dans Spark 2.0 +
      • Comme indiqué dans le ticket jira, la solution actuelle consiste à
        1. Changez votre code pour utiliser s3a et Hadoop 2.7.2+; c'est mieux tout autour, s'améliore dans Hadoop 2.8 et est la base de s3guard
        2. Utilisez Hadoop FileOutputCommitter et définissez mapreduce.fileoutputcommitter.algorithm.version sur 2

    -La fusion de schéma est désactivée par défaut à partir de Spark 1.5 Désactivez la fusion de schémas. Si la fusion de schémas est activée, le nœud du pilote analysera tous les fichiers pour garantir un schéma cohérent. Ceci est particulièrement coûteux car il ne s'agit pas d'une opération distribuée. Assurez-vous que cela est désactivé en faisant

    val file = sqx.read.option("mergeSchema", "false").parquet(path)

18
David

Le committer de sortie directe a disparu de la base de code spark; vous devez écrire le vôtre/ressusciter le code supprimé dans votre propre JAR. Si vous le faites, désactivez la spéculation dans votre travail et sachez que d'autres échecs peuvent également causer des problèmes, où le problème est "des données invalides".

Sur une note plus lumineuse, Hadoop 2.8 va ajouter des accélérations S3A spécifiquement pour la lecture de formats binaires optimisés (ORC, Parquet) sur S3; voir HADOOP-11694 pour plus de détails. Et certaines personnes travaillent sur l'utilisation d'Amazon Dynamo pour le magasin de métadonnées cohérent qui devrait être en mesure de faire un solide commit O(1)) à la fin du travail.

4
Steve Loughran

L'une des approches immédiates pour accélérer Spark écrit dans S3 est d'utiliser EMRFS S3-optimisé Committer .

Cependant, si vous utilisez s3a, ce committer ne peut pas être utilisé :

Lorsque le Committer optimisé pour EMRFS S3 n'est pas utilisé

Le committer n'est pas utilisé dans les circonstances suivantes:

When writing to HDFS

-> When using the S3A file system

When using an output format other than Parquet, such as ORC or text

When using MapReduce or Spark's RDD API

J'ai testé cette différence sur AWS EMR 5.26, et l'utilisation de s3: // était 15% -30% plus rapide que s3a: // (mais toujours lente).

0
jmng

J'ai également eu ce problème. En plus de ce que les autres ont dit, voici une explication complète d'AWS: https://aws.Amazon.com/blogs/big-data/improve-Apache-spark-write-performance-on-Apache-parquet -formats-with-the-emrfs-s3-optimized-committer /

Au cours de mon expérience, le passage à FileOutCommiter v2 (à partir de v1) a amélioré l'écriture 3-4x.

self.sc._jsc.hadoopConfiguration().set("mapreduce.fileoutputcommitter.algorithm.version", "2")
0
Sebastian Brestin