镶木地板格式的模式演变

Posted

技术标签:

【中文标题】镶木地板格式的模式演变【英文标题】:Schema evolution in parquet format 【发布时间】:2016-10-05 07:23:14 【问题描述】:

目前我们在生产中使用 Avro 数据格式。 在使用 Avro 的几个优点中,我们知道它在模式演化方面很有用。

现在我们正在评估 Parquet 格式,因为它在读取随机列时的效率。 因此,在继续前进之前,我们的关注点仍然是模式演变

有谁知道拼花地板中的模式演变是否可能,如果是如何可能,如果不是,那么为什么不可能。

一些resources 声称这是可能的,但它只能在末尾添加列

这是什么意思?

【问题讨论】:

【参考方案1】:

架构演化可能(非常)昂贵。

为了弄清楚架构,您基本上必须读取所有 parquet 文件并在读取期间协调/合并它们的架构,这可能会很昂贵,具体取决于数据集中有多少文件或/和多少列。

因此,从 Spark 1.5 开始,他们默认关闭了schema merging。您可以随时将其重新打开)。

由于模式合并是一项相对昂贵的操作,而不是 在大多数情况下,我们默认将其关闭,从 1.5.0。

如果没有架构演变,您可以从一个 parquet 文件中读取架构,并且在读取其余文件时假设它保持不变。

Parquet 模式演变取决于实现。

例如 Hive 有一个旋钮 parquet.column.index.access=false 您可以设置为按列名而不是按列索引映射架构。

那么你也可以删除列,而不仅仅是添加。

正如我上面所说,它依赖于实现,例如,Impala 无法正确读取此类拼花表(已在最近的 Impala 2.6 版本中修复)[Reference]。

从 2.0.2 版开始,Apache Spark 似乎仍然只支持添加列: [Reference]

用户可以从一个简单的架构开始,逐渐添加更多的列 根据需要添加到架构。这样,用户最终可能会得到多个 Parquet 文件具有不同但相互兼容的模式。这 Parquet 数据源现在能够自动检测这种情况并 合并所有这些文件的架构。

PS:我看到有些人为了更灵活地更改架构所做的事情是,他们在映射两个 (或更多)不同但兼容的模式到一个通用模式。

假设您在新版本中添加了一个新字段 (registration_date) 并删除了另一列 (last_login_date),则如下所示:

CREATE VIEW datamart.unified_fact_vw
AS
SELECT f1..., NULL as registration_date 
FROM datamart.unified_fact_schema1 f1
UNION ALL
SELECT f2..., NULL as last_login_date
FROM datamart.unified_fact_schema2 f2
;

你明白了。很高兴它在 Hadoop 方言上的所有 sql 中都可以正常工作(就像我在上面提到的 Hive、Impala 和 Spark),并且仍然具有 Parquet 表的所有优点(列存储、谓词下推等)。

P.P.S:添加一些关于 Spark 可以创建的 common_metadata 摘要文件的信息,以使这个答案更加完整。

看看 SPARK-15719

Parquet 摘要文件现在已经不是特别有用了,因为

 - when schema merging is disabled, we assume 
   schema of all Parquet part-files are identical, 
   thus we can read the footer from any part-files.

- when schema merging is enabled, we need to read footers 
  of all files anyway to do the merge.

On the other hand, writing summary files can be expensive,
because footers of all part-files must be read and merged. 
This is particularly costly when appending a small dataset 
to a large existing Parquet dataset.

所以有些观点反对启用common_metadata

当目录由混合了不同模式的 Parquet 文件组成时,_common_metadata 允许读者找出整个目录的合理模式,而无需读取每个单独文件的模式。由于 Hive 和 Impala 可以从 Hive 元存储访问所述文件的 SQL 模式,因此它们可以立即开始处理单个文件并在读取时将每个文件与 SQL 模式进行匹配,而不是事先探索它们的公共模式。这使得 Hive 和 Impala 不需要通用元数据功能。

即使 Spark 在没有 SQL 模式的情况下处理 Parquet 文件(除非使用 SparkSQL),因此理论上可以从 _common_metadata 中受益,但该功能仍然被认为没有用,因此在 SPARK-15719 中默认禁用。

即使此功能对查询很有用,但在编写过程中它仍然是一个负担。元数据必须维护,不仅速度慢,而且容易出现竞速条件和其他并发问题,缺乏原子性保证,并且容易因元数据陈旧或不一致而导致数据正确性问题。

该功能未记录在案,似乎被视为已弃用(仅“似乎是”,因为它似乎从来没有得到官方支持,并且不能弃用不受支持的功能要么)。

来自 Cloudera 的一位工程师:“我不知道如果存在 common_metadata 文件,读取端的行为是否已更改以避免查看每个页脚。但无论如何,将该文件写入首先是一个巨大的瓶颈,给我们的客户带来了很多问题。我强烈建议他们不要费心去尝试生成那个元数据文件。”

“_common_metadata”和“_metadata”文件是特定于 Spark 的,不是由 Impala 和 Hive 编写的,可能还有其他引擎。

Spark 中的摘要元数据文件可能仍然有它的用例——当没有上述并发和其他问题时——例如,一些流用例——我想这就是为什么这个功能没有从 Spark 中完全删除的原因。

【讨论】:

这表明 1) Parquet 本质上并不支持模式演化,并且文件阅读器是否能够理解嵌入在一组文件中的模式集? 2)我还假设没有外部模式文件,例如在 Avro 中找到的(avscavdl 等)? 如果我有整数列,后来它被转换为浮点数。我将如何适应这种架构更改 @teabot 我用summary-metadata 文件更新了答案。 @akashdeep float 是一种“通用”数据类型,可用于存储整数和浮点数。将旧表的整数转换为 float 与第二个表中的名称相同,并让第二个表保持原样浮动......你明白了。【参考方案2】:

除了上面的答案,其他选项是设置

"spark.hadoop.parquet.enable.summary-metadata" to "true"

它的作用:它会在您编写文件时使用架构创建摘要文件。保存后您将看到带有'_metadata''_common_metadata' 后缀的摘要文件。 _common_metadata 是每次读取 parquet 文件时都会读取的压缩模式。这使得读取速度非常快,因为您已经拥有架构。 Spark 会查找这些架构文件(如果存在)以获取架构。

请注意,这会导致写入速度非常慢,因为 Spark 必须合并所有文件的架构并创建这些架构文件。

我们遇到了类似的情况,即 parquet 架构发生了变化。我们所做的是在架构更改后的某个时间将上述配置设置为true,以便生成架构文件,然后将其设置回false。我们不得不在慢写上妥协一段时间,但在生成模式文件后,将其设置为 false 就可以达到目的。并且可以更快地读取文件。

【讨论】:

以上是关于镶木地板格式的模式演变的主要内容,如果未能解决你的问题,请参考以下文章

与其他格式相比,镶木地板格式的优缺点是啥?

雪花镶木地板加载模式生成

获取分区镶木地板数据帧的最新模式

在 Python 中即时从 CSV 生成镶木地板

我是不是需要将文件放入熊猫数据框中以放入镶木地板格式?

如何在读取前根据定义的模式读取 pyspark 中的镶木地板文件?