Apache Iceberg概述
一、what is Apache Iceberg?
为了解决数据存储和计算引擎之间的适配的问题,Netflix开发了Iceberg,2018年11月16日进入Apache孵化器,2020 年5月19日从孵化器毕业,成为Apache的顶级项目。
Apache Iceberg is an open table format for huge analytic datasets.Apache Iceberg 是一种适用于大型分析数据集的开放表格式。
表格式(Table Format)可以理解为元数据以及数据文件的一种组织方式,处于计算框架(Flink,Spark…)之下,数据文件之上。
A good way to define a table format is a way to organize a dataset’s files to present them as a single “table”.
Iceberg 使用高性能表格式将表添加到计算引擎中,包括 Spark、Trino、PrestoDB、Flink、Hive 和 Impala,其工作方式与 SQL 表类似。
二、Apache Iceberg核心特性
2.1 数据存储、计算引擎插件化(解耦)
- Apache Iceberg 作为一种开放的表格式,允许用户灵活地选择数据存储和计算引擎。
-
它与具体的存储解决方案(如 HDFS、Amazon S3)和计算引擎(如 Apache Spark、Apache Flink)解耦,使得用户能够根据需求随时调整架构。
-
例如,在一个数据分析平台中,用户可以将数据存储在 Amazon S3 中,并使用 Apache Spark 来进行批量分析,同时也能接入 Apache Flink 进行实时流处理,无需更改数据格式。
2.2 实时流批一体
- Iceberg 支持同时处理流数据和批数据,提供统一的读写接口。
- 可以在同一个数据管道中混合处理实时流数据(如 IoT 设备产生的数据)和历史批数据(如日志文件)。
- 例如,一个电商平台可以使用 Iceberg 接收实时的交易数据流,并同时执行对历史订单数据的分析,以生成实时销售报告和库存监控,简化 ETL 过程,减少数据延迟。
2.3 数据表演化(Table Evolution)
-
Apache Iceberg 提供了一种高效的方式来进行表级别的模式演进,使得用户能够灵活地修改表的结构而不需要耗费大量时间和资源。
-
这种模式演进通过简单的 SQL 操作实现,避免了繁琐的数据迁移和重写过程。
- 低代价的操作:在 Iceberg 中,模式演进几乎不需要重写数据。当用户需要对表的结构进行修改(如添加、删除列或更改列的数据类型)时,只需执行相应的 SQL 命令,Iceberg 会在后台自动处理相关的元数据更新,而不需要将现有数据读取出来再写入。
- 无缝集成:与传统的 Hive 表相比,Iceberg 允许用户在不影响现有查询的情况下进行修改。例如,用户可以在保留原有数据的基础上,逐步演进到新的数据模型。
假设一个在线零售平台的订单表最初按天分区:
CREATE TABLE orders (
order_id BIGINT,
customer_id BIGINT,
order_date DATE,
amount DECIMAL(10, 2)
) PARTITIONED BY (order_date);
如果业务需求发生变化,需要将表的分区方式改为按小时分区。在传统的 Hive 中,这通常需要创建新表并将数据插入其中:
- 创建新表:
CREATE TABLE orders_hourly (
order_id BIGINT,
customer_id BIGINT,
order_date DATE,
order_hour INT,
amount DECIMAL(10, 2)
) PARTITIONED BY (order_date, order_hour);//按小时分区
- 将旧表数据迁移到新表:
INSERT INTO orders_hourly
SELECT order_id, customer_id, order_date, HOUR(order_date) as order_hour, amount
FROM orders;
- 然后重命名新表以替换旧表,这可能影响其他依赖于旧表的查询。
而在 Iceberg 中,用户只需执行以下 SQL 操作:
ALTER TABLE orders ADD COLUMN order_hour INT;
ALTER TABLE orders SET PARTITIONED BY (order_date, order_hour);
Iceberg 会在不影响现有数据的情况下处理分区的变更,原有的数据仍然可以通过新的查询访问。这样,整个演进过程变得简单、快速且高效,大大降低了操作复杂性和风险。
数据架构的演进变得灵活和高效,企业可以更快速地响应业务需求的变化,保持数据模型的现代化。这种无缝的演进能力是许多企业在进行数据管理和分析时所期望的。
2.4 模式演化(Schema Evolution)
Iceberg 的模式演化设计提供了一种灵活、安全的方式来管理表结构,确保在修改表时不会影响现有数据或引入潜在的错误。
-
ADD:通过增加新列,用户可以轻松扩展表的结构,例如添加新特性而不干扰现有数据。
-
DROP:移除不再需要的列,确保数据模型的整洁,且不影响其他列的内容。
-
RENAME:重命名列有助于提升可读性或反映业务变化,Iceberg 保证其他列的数据不会受到影响。
-
UPDATE:对复杂结构的基本类型进行扩展,如从
tinyint
更新为int
,支持更大的数据范围,而无需重写数据。 -
REORDER:改变列的排列顺序,使数据结构更符合用户需求,同时保持数据完整性。
Iceberg 使用唯一ID来定位列,从而避免因名称重复或位置依赖引发的问题,这一设计增强了模式演化的灵活性和可靠性。
使用名称或者位置信息来定位列的, 都会存在一些问题, 比如使用名称的话,名称可能会重复, 使用位置的话, 不能修改顺序并且废弃的字段也不能删除。
2.5 分区演化(Partition Evolution)
- Iceberg可以在一个已存在的表上直接修改,因为Iceberg的查询流程并不和分区信息直接关联。
- 当我们改变一个表的分区策略时,对应修改分区之前的数据不会改变, 依然会采用老的分区策略,新的数据会采用新的分区策略。
- 也就是说同一个表会有两种分区策略,旧数据采用旧分区策略,新数据采用新新分区策略, 在元数据里两个分区策略相互独立,不重合。
- booking_table表2008年按月分区,进入2009年后改为按天分区,这两中分区策略共存于该表中。
- 借助Iceberg的隐藏分区(Hidden Partition),在写SQL 查询的时候,不需要在SQL中特别指定分区过滤条件,Iceberg会自动分区,过滤掉不需要的数据。
- Iceberg分区演化操作同样是一个元数据操作, 不会重写数据文件。
表 sales
,初始的分区策略是按年份(year
)进行分区:
id
amount
date
(包含年份信息)
初始分区策略:
year
分区- 数据分区示例:
- 2021
- 2022
- 2023
- 数据分区示例:
-
初始数据加载:
- 数据按
year
分区存储在表中。 - 例如,2022年的销售数据存储在
year=2022
的目录下。
- 数据按
-
修改分区策略:
-
假设将分区策略改为按月(
year/month
)进行分区。 -
新的分区策略会将数据细分为每个月。
-
现在表结构将会有两个分区策略。
-
2021年、2022年、2023年的旧数据仍然采用老的分区策略,即按
year
分区。 -
这些数据会保留在原有的分区结构中,例如:
year=2021
year=2022
year=2023
-
新加载的数据将会使用新的分区策略,即按
year/month
进行分区。 -
例如,2023年4月的新数据将存储在:
year=2023/month=04
的目录下。
-
Iceberg 确保查询可以有效处理这两种不同的分区策略:
- 查询旧数据:查询在
year
分区下的数据时,Iceberg 会直接访问原来的分区。 - 查询新数据:查询在
year/month
分区下的新数据时,Iceberg 会访问新的分区目录。
2.6 列顺序演化(Sort Order Evolution)
-
Iceberg可以在一个已经存在的表上修改排序策略。修改了排序策略之后, 旧数据依旧采用老排序策略不变。
-
往Iceberg里写数据的计算引擎总是会选择最新的排序策略, 但是当排序的代价极其高昂的时候, 就不进行排序了。
-
类似于:
orders
表,初始的排序策略是按customer_id
排序: -
order_id
-
customer_id
-
order_date
初始排序策略:按 customer_id
排序。
修改排序策略:
-
假设将排序策略更改为按
order_date
排序。 -
新的排序策略生效后,任何新写入的数据将采用
order_date
排序。 -
已存在的数据仍然保持按
customer_id
排序,不会受到影响。 -
计算引擎在写入新数据时会优先选择
order_date
排序策略。 -
在某些情况下,如果排序代价过高,可能会选择不进行排序。
在查询时,Iceberg 能够智能处理不同的排序策略:
- 查询旧数据:针对旧数据的查询依然能保持按
customer_id
的排序。 - 查询新数据:新数据会根据
order_date
进行排序。
2.7 隐藏分区(Hidden Partition)
Iceberg 提供了一种灵活的隐藏分区机制,使得用户无需手动管理分区信息。
- 自动分区管理:
- 隐蔽性:与传统的 Hive 分区策略不同,Iceberg 的分区字段和策略不需要暴露给用户。用户在创建表时或修改分区策略时,Iceberg 会自动计算新数据应归属的分区。
- 分区字段与表数据的独立性:
- 非表字段:分区字段可以是通过某种逻辑计算得出的,而不是直接表中的字段。分区策略可以更加灵活和动态。
- 目录无关性:Iceberg 的分区信息与表的数据存储目录是独立的,用户不需要关心物理存储的结构。
- 数据插入与查询:
- 自动计算:在插入新数据时,Iceberg 会根据定义的分区策略自动计算并归入相应的分区。
- 查询优化:在查询过程中,用户只需专注于业务逻辑,Iceberg 会自动过滤不需要的分区,从而提高查询性能。
- 修改分区策略:
- 灵活性:用户可以随时修改分区策略,而不需要进行数据迁移。Iceberg 处理了分区的重新计算和数据组织,确保数据的完整性和可用性。
2.8 时间旅行(Time Travel)
Iceberg提供了查询表历史某一时间点数据镜像(snapshot)的能力。
用户可以通过简单的查询语法,指定时间戳或快照 ID 来访问这些历史数据。
通过该特性可以将最新的SQL逻辑,应用到历史数据上。
- 审计与合规:查看过去某一时刻的数据状态,便于数据审计和合规检查。
- 数据恢复:在数据错误或丢失时,可以恢复到之前的版本。
- 比较变更:分析数据的历史变化,便于理解数据演变过程。
2.9 支持事务(ACID)
Iceberg 的 ACID 事务支持使得数据管理更加可靠。
通过允许并发读写,Iceberg 能够实现高效的 upsert 操作(允许用户同时执行插入和更新),确保下游组件只接收到已提交的数据。
2.10 基于乐观锁的并发支持
乐观锁机制:
-
Iceberg 使用乐观并发控制来处理多个程序的并发写入。在每次事务开始时,Iceberg 不会立即锁定数据,而是允许所有操作并在提交时检查数据的完整性。
-
当一个事务准备提交时,它会检查是否有其他事务已更改了相同的数据。如果没有冲突,事务就会成功提交;如果有冲突,则会被拒绝,用户需要重新尝试。
-
减少锁竞争:由于不在读操作时加锁,乐观锁机制降低了在高并发环境中出现的竞争条件。
-
线性一致性:尽管是乐观的控制方式,Iceberg 仍能保证数据的一致性,使得多个并发写入不会导致脏读或不可重复读。
2.11 文件级数据剪裁
-
Iceberg 在每个数据文件的元数据中维护统计信息,例如最小值、最大值、计数等。这些信息使得查询优化器能够更智能地选择需要读取的文件。
-
在执行查询时,查询引擎可以根据 SQL 语句中的过滤条件,评估每个文件的统计信息,从而决定是否需要读取该文件。
-
加快查询速度:通过剪裁不相关的数据文件,Iceberg 可以显著减少需要扫描的数据量,从而加快查询速度。
-
资源优化:减少 I/O 操作,节省计算资源,提升整体性能。