PySpark解压缩文件:解压缩文件并将csv文件存储到Delta表中的一种好方法?

时间:2019-10-30 15:20:51

标签: python amazon-s3 zip azure-databricks delta-lake

我有存储在Amazon s3中的zip文件,然后有一个Python列表为["s3://mybucket/file1.zip", ..., "s3://mybucket/fileN.zip"],我需要使用Spark Cluster解压缩所有这些文件,并将所有CSV文件存储到增量格式表中。我想知道一种比当前方法更快的处理方法:

1)我有一个 bucle 用于在我的Python列表中进行迭代。

2)我正在使用Python Boto3 s3.bucket.Object(file)

从s3获取zip文件。

3)我正在使用下一个代码解压缩文件

import io
import boto3
import shutil
import zipfile
for file in ["s3://mybucket/file1.zip", ..., "s3://mybucket/fileN.zip"]:
    obj = s3.bucket.Object(file)
    with io.BytesIO(obj.get()["Body"].read()) as tf:
        tf.seek(0)
        with zipfile.ZipFile(tf, mode='r') as zipf:
            for subfile in zipf.namelist():
                zipf.extract(subfile, outputZip)
    dbutils.fs.cp("file:///databricks/driver/{0}".format(outputZip), "dbfs:" + outputZip, True)
    shutil.rmtree(outputZip)
    dbutils.fs.rm("dbfs:" + outputZip, True)

4)我的文件在驱动程序节点中解压缩,然后执行者无法访问这些文件(我找不到解决方法),因此我使用dbutils.fs.cp()将所有这些csv文件移至DBFS

5)我使用Pyspark数据框从DBFS读取了所有csv文件,并将其写入Delta表

df = self.spark.read.option("header", "true").csv("dbfs:" + file) 
df.write.format("delta").save(path)

6)我从DBFS和驱动程序节点中删除数据

因此,我当前的目标是比以前的过程花费更少的时间将S3中的zip文件提取到Delta表中。我想我可以将其中一些过程并行化为1)步骤,我想避免复制到DBFS的步骤,因为我不需要在那里保存数据,而且每次提取到CSV文件后都需要删除它们增量表以避免驱动程序节点磁盘中的内存错误。有什么建议吗?

2 个答案:

答案 0 :(得分:1)

嗯,可能有多种解决方案:

  1. 您可以使用df=spark.read.csv("s3://mybucket")一起读取所有文件(如果架构允许的话),并使用df.write.format("delta").save(path) 将数据帧写为增量数据
  2. 您可以在数据框中单独读取每个文件,然后直接将其附加到现有的增量表中(即使它为空),而无需将其存储在DBFS中。有关更多详细信息:https://docs.databricks.com/delta/delta-batch.html#append-using-dataframes
  3. 您可以在数据框中单独读取每个文件,然后将其合并到现有的主数据框中。最后,您可以将主数据帧写入增量表。

选项3类似于:

    import io
    import boto3
    import shutil
    import zipfile
    from pyspark.sql import SparkSession

    spark = SparkSession.builder.appName("name").getOrCreate()

    schema = StructType([
    \\ YOUR DATA SCHMEA
    ])

    df = spark.createDataFrame([], schema)

    for file in ["s3://mybucket/file1.zip", ..., "s3://mybucket/fileN.zip"]:
        obj = s3.bucket.Object(file)
        with io.BytesIO(obj.get()["Body"].read()) as tf:
            tf.seek(0)
            with zipfile.ZipFile(tf, mode='r') as zipf:
                for subfile in zipf.namelist():
                    zipf.extract(subfile, outputZip)
        tempdf = spark.read.option("header", "true").csv(outputZip)
        df = df.union(tempdf)      

    df.write.format("delta").save(path)

答案 1 :(得分:0)

众所周知,

Zip不是一种可拆分的压缩技术,没有可用于zip的内置编解码器。您可能会发现一些聪明的人已经编写了自己的Spark邮政编码编解码器/数据源,但我还没有找到它。

我最近得到的一些提示:

  1. aws cli会将文件并行复制到本地节点(驱动程序或工作程序)。运行aws cp <src> <dst>比使用aws cp <src> -流二进制文件要快得多。 aws cli / botodbutils.fs.cp()

    快得多(但难用)
  2. 使用for循环是非并行执行和效率低下的一个好兆头。这将导致较低的吞吐量,较高的成本和较低的群集利用率。尽可能使用Spark和Python的功能。

  3. 您可以使用以下内容创建文件名称的Spark DataFrame:

df_files = spark.createDataFrame(dbutils.fs.ls("<s3 path to bucket and folder>"))

在此数据框中,您可以运行puds udf,.pipe()运算符和常规UDF。

  1. 在一组工作程序上并行运行spark .pipe()或Pandas UDF,每个工作在文件路径之外的程序都将使您获得更大的吞吐量,并使群集保持繁忙。 >

  2. 使用PandasUDF执行的实验: 一种。复制-解压缩-复制回s3,使用CSV阅读器阅读 b。复制解压后在熊猫udf中返回。 观看Ganglia指标以确保高利用率。

  3. 由于要写入Delta表,因此如果必须具有多个spark作业,则可以可靠地并行运行写入(追加)。 [一项工作中的并行工人>>并行火花作业]

  4. 向CSV提供模式的读取速度比.option("inferSchema","true")快2倍