镶木地板内件和火花
Parquet Internals & Spark
我有一个数据框,该数据框由运行特定日期的每日批处理创建,然后保存在 HDFS(Azure Data Lake Gen 2)中。
它是用这样的东西保存的
TradesWritePath="abfss://refined@"+datalakename+".dfs.core.windows.net/curated/clientscoredata/clientscoredatainput/Trades/" + TradeDatedt + "/Trades_" + tradedateInt + ".parquet"
tradesdf.write.mode("overwrite").parquet(TradesWritePath)
如您所见,我没有对数据帧进行分区,因为它只包含一个日期。
因此,作为示例,第一天的第一个文件将存储在文件夹中
Trades/2019/08/25
然后第二天,它将在文件夹中
Trades/2019/08/26
问题是,当所有数据都已放入后,日期的过滤谓词是否仍会被下推,HDFS 是否会知道在哪里找到数据而不是进行全面扫描?
或者即使我节省了一天时间,我仍然必须使用 Partition by option 来编写,这样 Spark 就可以在阅读时理解并将其推送到 HDFS 并且 HDFS 也知道在哪里可以找到它(而不是全扫描) ?
问题的下一部分是:
当我查看存储 parquet 文件的文件夹时,如果我必须减少文件数量,我会看到很多小的 "part-****.snappy.parquet files. I understand by reading some of the forum questions here that I have to use "repartition" 选项。
但问题是——有必要吗?我读到过多的小文件当然会影响性能,因此一种选择是将其保存为 128 MB 的块(如果我不确定数据将如何在下游使用,即目前哪些列),这样 HDFS 中的 NameNode 就不会负担过重,我们也不会有太大的文件。这也适用于这些 "snappy parquet partition files" 吗?
概念太多,我仍在努力寻找将这些数据帧存储为镶木地板的最佳解决方案,所以任何见解都将不胜感激。
如果数据存储为
,Spark 会知道从哪里获取数据
root/
date=ddmmyy/
date=dd1mm1yy1/
...
=
符号很重要。谓词下推不能有任意目录结构。它必须采用上述格式。
在你的例子中
您需要存储类似
的内容
root/
Trades=2019/08/25
Trades=2019/08/26
Spark 利用 Hive 分区发现机制来检测 table 中的分区。 Hive分区需要数据以特定的方式铺设。
进入你问题的下一部分。无论文件类型如何,将小文件保存在 HDFS
中都是非常糟糕的。是的,活泼的分区文件也是如此。您应该使用 repartition
或 coalesce
函数使文件大小接近 128 MB。
namenode的职责是跟踪HDFS中的所有文件。 HDFS 中的块大小为 128 MB。因此,请尽量将 .parquet
文件大小保持在接近 128 MB 但不要更大。如果你保留更多,HDFS 将使用 2 个块来表示数据。
您不应创建自己的目录。
在编写 parquet 时使用 partition
按日期分区。它将自动处理目录创建,并且在读取时不会扫描完整的table。
对于你问题的第二部分,是的,想法是将每个分区保持在 128MB 左右,但老实说,这不会花费你太多,你可以将默认分区保持在 200。
我有一个数据框,该数据框由运行特定日期的每日批处理创建,然后保存在 HDFS(Azure Data Lake Gen 2)中。
它是用这样的东西保存的
TradesWritePath="abfss://refined@"+datalakename+".dfs.core.windows.net/curated/clientscoredata/clientscoredatainput/Trades/" + TradeDatedt + "/Trades_" + tradedateInt + ".parquet"
tradesdf.write.mode("overwrite").parquet(TradesWritePath)
如您所见,我没有对数据帧进行分区,因为它只包含一个日期。
因此,作为示例,第一天的第一个文件将存储在文件夹中
Trades/2019/08/25
然后第二天,它将在文件夹中
Trades/2019/08/26
问题是,当所有数据都已放入后,日期的过滤谓词是否仍会被下推,HDFS 是否会知道在哪里找到数据而不是进行全面扫描?
或者即使我节省了一天时间,我仍然必须使用 Partition by option 来编写,这样 Spark 就可以在阅读时理解并将其推送到 HDFS 并且 HDFS 也知道在哪里可以找到它(而不是全扫描) ?
问题的下一部分是:
当我查看存储 parquet 文件的文件夹时,如果我必须减少文件数量,我会看到很多小的 "part-****.snappy.parquet files. I understand by reading some of the forum questions here that I have to use "repartition" 选项。 但问题是——有必要吗?我读到过多的小文件当然会影响性能,因此一种选择是将其保存为 128 MB 的块(如果我不确定数据将如何在下游使用,即目前哪些列),这样 HDFS 中的 NameNode 就不会负担过重,我们也不会有太大的文件。这也适用于这些 "snappy parquet partition files" 吗?
概念太多,我仍在努力寻找将这些数据帧存储为镶木地板的最佳解决方案,所以任何见解都将不胜感激。
如果数据存储为
,Spark 会知道从哪里获取数据root/
date=ddmmyy/
date=dd1mm1yy1/
...
=
符号很重要。谓词下推不能有任意目录结构。它必须采用上述格式。
在你的例子中
您需要存储类似
的内容 root/
Trades=2019/08/25
Trades=2019/08/26
Spark 利用 Hive 分区发现机制来检测 table 中的分区。 Hive分区需要数据以特定的方式铺设。
进入你问题的下一部分。无论文件类型如何,将小文件保存在 HDFS
中都是非常糟糕的。是的,活泼的分区文件也是如此。您应该使用 repartition
或 coalesce
函数使文件大小接近 128 MB。
namenode的职责是跟踪HDFS中的所有文件。 HDFS 中的块大小为 128 MB。因此,请尽量将 .parquet
文件大小保持在接近 128 MB 但不要更大。如果你保留更多,HDFS 将使用 2 个块来表示数据。
您不应创建自己的目录。
在编写 parquet 时使用 partition
按日期分区。它将自动处理目录创建,并且在读取时不会扫描完整的table。
对于你问题的第二部分,是的,想法是将每个分区保持在 128MB 左右,但老实说,这不会花费你太多,你可以将默认分区保持在 200。