表

分区

师成师成· 更新于 2026-09-28· 阅读 5 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

分区

什么是分区?

分区是一种在写入时将相似的行分组在一起,从而使查询更快的方式。

例如,查询 logs 表中的日志条目通常会包含一个时间范围,比如查询上午 10 点到 12 点之间的日志:

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 可以按年、月、日、小时等粒度对时间戳进行分区。它还可以使用分类列(例如本日志示例中的 level)将行存储在一起,从而加快查询速度。

Iceberg 有什么不同之处?

其他表格式(如 Hive)也支持分区,但 Iceberg 支持的是隐式分区(hidden partitioning)。

  • Iceberg 负责处理为表中各行生成分区值这一繁琐且易出错的任务。
  • Iceberg 会自动跳过不必要的分区。使用方无需了解表的分区方式,也不必在查询中添加额外的过滤条件。
  • Iceberg 的分区布局可以按需演进。

Hive 中的分区

为了说明两者的差异,我们来看看 Hive 会如何处理 logs 表。

在 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;

同样,查询 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 必须由外部提供分区值。在日志示例中,它不知道 event_time 与 event_date 之间的关系。

这会导致几个问题:

  • Hive 无法校验分区值——是否产生正确的值完全取决于写入方

    • 使用错误的格式,例如写成 2018-12-01 而不是 20181201,会产生静默的错误结果,而不是查询失败
    • 使用错误的源列(如 processing_time)或时区,同样会导致错误结果而非失败
  • 查询是否正确完全由用户负责

    • 使用错误的格式同样会导致静默的错误结果
    • 不了解表物理布局的用户会得到不必要地变慢的查询——Hive 无法自动转换过滤条件
  • 可用的查询与表的分区方案绑定在一起,因此分区配置一旦更改就会破坏查询

Iceberg 的隐藏分区

Iceberg 通过获取列值并对其(可选地)施加变换来生成分区值。将 event_time 转换为 event_date 的工作由 Iceberg 负责,并且它会记录这一关系。

表的分区就是基于这些关系进行配置的。logs 表将按 day(event_time) 和 level 进行分区。

由于 Iceberg 不需要用户维护分区列,因此可以隐藏分区。分区值每次都能被正确生成,并且在可能的情况下总是被用来加速查询。生产者和消费者甚至不会看到 event_date 的存在。

最重要的是,查询不再依赖表的物理布局。通过物理布局与逻辑布局的分离,Iceberg 表可以随着数据量的变化随时间演进分区方案。配置错误的表无需昂贵的迁移即可修复。

有关所有受支持的隐藏分区变换的详细信息,请参阅分区变换部分。

有关更新表分区规范的详细信息,请参阅分区演进部分。

评论

登录后参与评论

正在加载评论…