按时间戳分区好还是按年月日小时分区好
Is it better to partition by time stamp or year,month,day, hour
我正在开发一个 spark 应用程序,它将处理后的数据写入 parquet 文件,并且对数据的查询总是关于一个时间段。因此,我想按日期时间对其进行分区。这两种方法哪种更好?
数据帧:|创建时间(java.sql.Timestamp)|数据(字符串)|
1) dataframe.write.partitionBy("CreationTime").parquet(path)
2) dataframe.withColumn("year", year("CreationTime"))
.withColumn("month", month("CreationTime"))
.withColumn("day", day("CreationTime"))
.write.partitionBy("year","month","day").parquet(path)
在第二种情况下,reader 变得复杂,它必须执行 startTime.year > col("year") and endTime.year < col("year)
并且类似地处理月份和日期。
在这种情况下,对数据进行分区的常见做法和建议是什么?
以每 10 分钟 1GB 为目标意味着您将很快积累大量数据(每周 1000 个文件和 1TB,或多或少)。
您的选择至少要考虑到:
- 您必须支持的查询(它们 - 几乎 - 总是有时间范围,还是从来没有?)
- 您要将其转储到的存储(您是否必须跨多个数据中心/S3 存储桶/...)
- 您的分区方案将添加到您的数据之上的大小
例如,创建一个年列、一个月列、一个天列、一个小时列和一个分钟列意味着为您的每条记录创建 5 个列,每个列都是数字类型。也许它可以忽略不计,也许根本不是。并且您必须为写入时增加的存储空间、相关带宽和 CPU 在读取时解析它支付费用,因为这些数据无助于构建任何功能。
另一方面,这将是一种非常可读/可调试的存储此数据的方式。
您可以有另一个更简单的策略,即用一个数字表示每 10 分钟的帧:yourTimestampInMillisSinceEpoch / TimeUnit.MINUTES.convert(10, TimeUnit.MILLISECONDS)
。那只会花费你一个号码。
我会推荐这个 "single column" 策略,但我不会就此止步。
在实际将文件写入单个路径之前,我会按天和按月拆分数据框。并不是说我实际上需要存储日期和月份(我会在写入结果之前删除它们),但我会使用它们来构建我的 parquet 文件夹路径,类似于 (hdfs or S3, etc...)://your_root/{year}/{month}/{day}/
。 (一种特别有效的方法是利用 partition discovery at the spark level,它允许 Spark 使用基于它的 "infer" "virtual" 列的目录路径,并相应地优化查询)。
这将允许我,如果我需要在一个时间范围内查询,利用 ,以便甚至不开始读取没有机会保存给定时间的任何有趣数据的文件范围。
将所有数据转储到单个路径会阻止此优化,您需要列出一个非常大的目录,然后打开其中的每个文件以查看它保存的时间 window,这将是一个CPU、带宽、性能(以及,是的,在未来,金钱)的巨大浪费。
我正在开发一个 spark 应用程序,它将处理后的数据写入 parquet 文件,并且对数据的查询总是关于一个时间段。因此,我想按日期时间对其进行分区。这两种方法哪种更好?
数据帧:|创建时间(java.sql.Timestamp)|数据(字符串)|
1) dataframe.write.partitionBy("CreationTime").parquet(path)
2) dataframe.withColumn("year", year("CreationTime"))
.withColumn("month", month("CreationTime"))
.withColumn("day", day("CreationTime"))
.write.partitionBy("year","month","day").parquet(path)
在第二种情况下,reader 变得复杂,它必须执行 startTime.year > col("year") and endTime.year < col("year)
并且类似地处理月份和日期。
在这种情况下,对数据进行分区的常见做法和建议是什么?
以每 10 分钟 1GB 为目标意味着您将很快积累大量数据(每周 1000 个文件和 1TB,或多或少)。
您的选择至少要考虑到:
- 您必须支持的查询(它们 - 几乎 - 总是有时间范围,还是从来没有?)
- 您要将其转储到的存储(您是否必须跨多个数据中心/S3 存储桶/...)
- 您的分区方案将添加到您的数据之上的大小
例如,创建一个年列、一个月列、一个天列、一个小时列和一个分钟列意味着为您的每条记录创建 5 个列,每个列都是数字类型。也许它可以忽略不计,也许根本不是。并且您必须为写入时增加的存储空间、相关带宽和 CPU 在读取时解析它支付费用,因为这些数据无助于构建任何功能。
另一方面,这将是一种非常可读/可调试的存储此数据的方式。
您可以有另一个更简单的策略,即用一个数字表示每 10 分钟的帧:yourTimestampInMillisSinceEpoch / TimeUnit.MINUTES.convert(10, TimeUnit.MILLISECONDS)
。那只会花费你一个号码。
我会推荐这个 "single column" 策略,但我不会就此止步。
在实际将文件写入单个路径之前,我会按天和按月拆分数据框。并不是说我实际上需要存储日期和月份(我会在写入结果之前删除它们),但我会使用它们来构建我的 parquet 文件夹路径,类似于 (hdfs or S3, etc...)://your_root/{year}/{month}/{day}/
。 (一种特别有效的方法是利用 partition discovery at the spark level,它允许 Spark 使用基于它的 "infer" "virtual" 列的目录路径,并相应地优化查询)。
这将允许我,如果我需要在一个时间范围内查询,利用
将所有数据转储到单个路径会阻止此优化,您需要列出一个非常大的目录,然后打开其中的每个文件以查看它保存的时间 window,这将是一个CPU、带宽、性能(以及,是的,在未来,金钱)的巨大浪费。