我正在尝试使用parquet
将Amazon S3
文件写到Spark 1.6.1
。我生成的小parquet
一旦写入~2GB
,所以数据并不多。我试图将Spark
作为我可以使用的平台来证明。
基本上我要做的是设置star schema
dataframes
,然后我要把那些桌子写成实木复合地板。数据来自供应商提供的csv文件,我使用Spark作为ETL
平台。我目前在ec2(r3.2xlarge)
中有一个3节点集群,因此执行器上的内存120GB
总共有16个内核。
输入文件总共大约22GB,我现在正在提取大约2GB的数据。最后,当我开始加载完整数据集时,这将是几TB。
这是我的spark / 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")
}
465884512行的计数大约需要2分钟。对拼花的写入需要 38分钟
据我所知,coalesce
对写作的驱动程序进行了随机播放....但是它所花费的时间让我觉得我做错了。如果没有coalesce
,这仍然需要15分钟,IMO仍然太长,并且给了我大量的parquet
文件。我想每天有一个大文件,我将拥有。我有代码来执行按字段值进行分区,而且速度也很慢。我也尝试将此输出到csv
,这需要约1小时。
另外,当我提交作业时,我并没有真正设置运行时道具。我的一项工作的控制台统计信息是:
答案 0 :(得分:18)
Spark默认值会在I / O操作期间导致大量(可能)不必要的开销,尤其是在写入S3时。 This article对此进行了更全面的讨论,但您有两种设置需要考虑更改。
使用DirectParquetOutputCommitter。默认情况下,Spark会将所有数据保存到临时文件夹,然后再移动这些文件。使用DirectParquetOutputCommitter将通过直接写入S3输出路径来节省时间
- 将您的代码切换为使用s3a和Hadoop 2.7.2+;它全面变得更好,在Hadoop 2.8中变得更好,并且是s3guard的基础
- 使用Hadoop FileOutputCommitter并将mapreduce.fileoutputcommitter.algorithm.version设置为2
醇>
- 默认情况下,自Spark 1.5 关闭模式合并时关闭模式合并。如果启用了架构合并,则驱动程序节点将扫描所有文件以确保一致的架构。这特别昂贵,因为它不是分布式操作。通过执行
val file = sqx.read.option("mergeSchema", "false").parquet(path)
答案 1 :(得分:4)
直接输出提交器已从spark代码库中消失;你要在自己的JAR中编写自己的/恢复已删除的代码。如果你这样做,在你的工作中关闭推测,并知道其他失败也会导致问题,问题是"数据无效"。
更明亮的是,Hadoop 2.8将增加一些S3A加速,专门用于读取S3的优化二进制格式(ORC,Parquet);有关详细信息,请参阅HADOOP-11694。有些人正在努力将Amazon Dynamo用于一致的元数据存储,该存储应该能够在工作结束时执行强大的O(1)提交。
答案 2 :(得分:0)
加快Spark向S3写入速度的一种直接方法是使用EMRFS S3-optimized Committer 。
但是,如果您使用s3a,则此提交者cannot be used:
未使用EMRFS S3优化的提交器时
在以下情况下不使用提交器:
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
我已经在AWS EMR 5.26上测试了这种差异,并且使用s3://比s3a://快15%-30%(但仍然很慢)。
我设法完成这种复制/写入的最快方法是将Parquet写入本地HDFS,然后使用s3distcp复制到S3。在一种特定情况下(几百个小文件),这比将DataFrame直接写入Parquet到S3快5倍。
答案 3 :(得分:0)
我也遇到了这个问题。除了其他人所说的以外,以下是AWS的完整解释:https://aws.amazon.com/blogs/big-data/improve-apache-spark-write-performance-on-apache-parquet-formats-with-the-emrfs-s3-optimized-committer/
在实验过程中,仅将FileOutCommiter v2(从v1更改为v2)可提高3-4倍的写入率。
self.sc._jsc.hadoopConfiguration().set("mapreduce.fileoutputcommitter.algorithm.version", "2")