发布日期:2022-10-26 VIP内容

隐藏分区和分区演变

与其他数据湖产品相比,Iceberg提供了对隐藏分区和分区演变的支持。在深入了解隐藏分区和分区演变之前,先来重温一下分区的概念。

分区概念

分区是一种编写时通过将相似的行分组在一起来加快查询速度的方法。例如,对logs日志表中的日志项的查询通常包含一个时间范围,例如对上午10点到12点之间的日志的查询,可以使用的SQL语句如下:

SELECT level, message FROM logs
WHERE event_time BETWEEN '2018-12-01 10:00:00' AND '2018-12-01 12:00:00'

将logs表配置为按event_time日期分区将会把日志事件分组到具有相同事件日期的文件中。Iceberg会跟踪这个日期,并使用它来跳过没有有用数据的其他日期的文件。

Iceberg可以按年、月、日和小时粒度对时间戳分区。它还可以使用分类列(如logs表中的level列,代表日志级别)将行存储在一起,从而加快查询速度。

与其他支持分区的表格式如Hive相比,Iceberg还支持隐藏分区。什么是隐藏分区?

(1) Iceberg负责处理为表中的行生成分区值的繁琐且容易出错的任务。

(2) Iceberg避免自动读取不必要的分区。使用者不需要知道表是如何分区的,也不需要在查询中添加额外的过滤器。

(3) Iceberg分区布局可以根据需要发展演化。

为了进一步理解Iceberg支持的隐藏分区,考虑一下Hive是如何实现和管理分区的。

Hive分区

在Hive中,分区是显式的,以列的形式出现,所以logs分区表中会有一个名为event_date的列。在每次写表时都必须提供分区,所以当写入时,insert需要为event_date列提供数据,代码如下:

INSERT INTO logs PARTITION (event_date)
  SELECT level, message, event_time, format_time(event_time, 'YYYY-MM-dd')
  FROM unstructured_log_source

类似地,Hive中的查询还必须为分区列显式地提供一个过滤器。例如在logs分区表中执行搜索的查询,除了需要提供event_time过滤器外,还必须具有event_date过滤器,代码如下:

SELECT level, count(1) as count FROM logs
WHERE event_time BETWEEN '2018-12-01 10:00:00' AND '2018-12-01 12:00:00'
   AND event_date = '2018-12-01'

如果缺少event_date过滤器,Hive会执行全表扫描(即扫描表中的每个文件),因为它不知道event_time列与event_date列相关。所以Hive分区的问题就在于,Hive必须被赋予分区值。在logs日志表中,它并不知道event_time和event_date之间的关系。

这导致了如下几个问题:

  • (1) Hive不能验证分区值—要生成正确的值取决于写入器writer。
  • (2) 如果使用错误的格式(例如2018-12-01而不是20181201),将产生静默的错误结果,而不是查询失败。
  • (3) 使用错误的源列(如processing_time或时区)也会导致不正确的结果,而不是失败。
  • (4) 正确地编写查询取决于用户。
  • (5) 使用错误的格式也会导致静默的错误结果。
  • (6) 不理解表的物理布局的用户会得到不必要的慢查询—Hive不能自动翻译过滤器。
  • (7) 工作查询被绑定到表的分区模式(scheme),因此不能在不破坏查询的情况下更改分区配置。

Iceberg隐藏分区

Iceberg通过获取列值并可选地转换它来生成分区值。Iceberg负责将event_time转换为event_date,并跟踪它们之间的关系。表分区是使用这些关系配置的。日志表logs将按照日期(event_time)和级别(level)进行分区。

因为Iceberg不需要用户维护的分区列,所以它可以隐藏分区。Iceberg通过实现隐藏分区为用户简化了分区。每次查询都会正确地生成分区值,并且总是在可能的情况下用于加速查询。Iceberg没有强制用户在查询时提供单独的分区过滤器,而是在底层处理分区和查询的所有细节。用户不需要维护分区列,甚至不需要理解物理表布局就可以得到准确的查询结果。

隐藏分区最重要的好处是,用户在查询Iceberg表时不需要提供分区布局信息,查询不再依赖于表的物理布局。通过物理和逻辑的分离,Iceberg表可以随着数据量的变化而演化分区模式。不需要进行昂贵的迁移就可以修复配置错误的表。这不仅使Iceberg分区非常友好,而且还允许在不破坏预先编写的查询的情况下随时间改变分区布局。在演化分区规范时,更改之前表中的数据不受影响,其元数据也不受影响。只有在演化之后写入表的数据才会用新规范进行分区,并且这个新数据集的元数据会单独保存。在查询时,每个分区布局的各自元数据被用来标识它需要访问的文件,这被称为拆分规划(split-planning)。拆分规划是Iceberg的众多特性之一,这些特性是由于表元数据而得以实现的,它创建了物理和逻辑之间的分离。

分区演变

Hive表不支持就地分区演变。在Hive中,要更改分区,必须使用新的分区列完全重写整个表。这对于大型表来说代价很高,并可能导致数据准确性问题。此外,依赖于表分区的查询现在必须为新表重写。而Iceberg以一种不会导致这些问题的方式实现分区。

Iceberg支持就地表进化,可以在数据量改变时改变分区布局。Iceberg不需要昂贵的迁移,比如重写表数据或迁移到新表。例如,因为Hive表分区不能改变,所以从按day分区布局到按hour分区布局就需要一个新表。而由于查询依赖于分区,因此必须为新表重写查询。

因为在Iceberg表中查询不直接引用分区值,所以分区可以在现有表中更新。当演变一个分区规范时,使用早期规范编写的旧数据保持不变。在新的布局中使用新规范写入新数据。每个分区版本的元数据是分开保存的。正因为如此,当用户开始编写查询时,就得到了拆分计划。在拆分计划中,每个分区布局使用它为特定分区布局派生的过滤器分别规划文件。这个过程如图11-38所示。

在图11-38中,2008年的数据按月分区,从2009年开始对表进行了更新,这样数据就可以按天分区了。这两种分区布局可以在同一个表中共存。

由于Iceberg使用了隐藏的分区,因此不需要为特定的分区布局编写查询来提高速度。相反,可以编写选择所需数据的查询,Iceberg会自动删除不包含匹配数据的文件。

分区演化是一种元数据操作,不会立即重写文件。Spark支持通过ALTER TABLE SQL语句更新分区规范。