Paimon 快照(Snapshot)全面详解
1. 快照基础概念
1.1 什么是快照
快照(Snapshot)是 Paimon 表在某个时间点的数据状态记录。它类似于数据库的事务日志点,记录了表在特定时刻的完整状态。每个快照都有一个唯一的 ID(通常是递增的整数),用于标识特定时间点的数据版本。
在 Paimon 中,快照具有以下特点:
- 不可变性:一旦创建,快照内容不会被修改
- 版本化:每个快照代表表的一个版本状态
- 增量存储:快照之间只存储数据变化部分,节省存储空间
- 时间旅行:可以查询任意快照点的数据,实现数据回溯功能
1.2 快照的作用
快照在 Paimon 中发挥着核心作用:
- 数据版本控制:跟踪表数据的历史变化
- 时间点恢复:在数据损坏或误操作时恢复到之前的状态
- 增量数据处理:支持从特定快照开始读取增量数据
- 并发控制:提供多版本并发控制机制
- 数据一致性:确保读取操作可以看到一致的数据视图
2. 快照参数详解
2.1 快照过期相关参数
| 参数名 | 类型 | 默认值 | 说明 | 示例 |
|---|---|---|---|---|
snapshot.time-retained | Duration | 7d | 快照保留的时间,超过该时间的快照将被删除 | 14d (保留14天) |
snapshot.num-retained.min | Integer | 1 | 保留的最小快照数量,即使超过时间也不会删除 | 3 (至少保留3个快照) |
snapshot.num-retained.max | Integer | 100 | 保留的最大快照数量,即使未超过时间也会删除最旧的 | 50 (最多保留50个快照) |
snapshot.retention-check-interval | Duration | 1h | 检查快照过期的时间间隔 | 30m (每30分钟检查一次) |
配置示例:
CREATE TABLE my_table (
id INT,
name STRING
) WITH (
'connector' = 'paimon',
'path' = 'hdfs:///path/to/table',
'snapshot.time-retained' = '30d',
'snapshot.num-retained.min' = '5',
'snapshot.num-retained.max' = '20'
);
2.2 快照创建相关参数
| 参数名 | 类型 | 默认值 | 说明 | 示例 |
|---|---|---|---|---|
snapshot.combine-target-size | String | 128MB | 快照合并的目标文件大小 | 256MB |
snapshot.max-concurrent-changes | Integer | 500 | 单个快照允许的最大并发变更数 | 1000 |
write-buffer-size | String | 64MB | 写入缓冲区大小,影响快照的生成频率 | 128MB |
write-buffer-spill-threshold | Double | 0.8 | 缓冲区溢出阈值,达到此值将触发快照创建 | 0.9 |
配置示例:
CREATE TABLE my_table (
id INT,
name STRING
) WITH (
'connector' = 'paimon',
'path' = 'hdfs:///path/to/table',
'snapshot.combine-target-size' = '256MB',
'write-buffer-size' = '128MB'
);
3. 快照策略形象化理解
3.1 快照过期策略
快照过期策略可以形象地理解为一个"滑动窗口"机制:
时间轴 ->
[快照1][快照2][快照3][快照4][快照5][快照6][快照7][快照8][快照9][快照10]...
^ ^ ^
| | |
时间过期边界 最小保留数量边界 最大保留数量边界
- 时间过期边界:超过`snapshot.time-retained`时间的快照将被删除
- 最小保留数量边界:即使超过时间,至少保留`snapshot.num-retained.min`个快照
- 最大保留数量边界:最多保留`snapshot.num-retained.max`个快照
实际场景举例:
- 配置:
time-retained=7d,num-retained.min=3,num-retained.max=10 - 效果:
- 所有超过7天的快照会被删除,但至少保留最新的3个快照
- 如果一周内创建了15个快照,只会保留最新的10个
- 如果30天只创建了2个快照,这2个快照都会保留,不会被删除
3.2 快照合并策略
快照合并是将多个小快照合并成较大快照的过程,可以形象地理解为"数据压缩":
[小快照1] + [小快照2] + [小快照3] = [大合并快照]
合并策略具有以下特点:
- 空间优化:减少元数据开销,节省存储空间
- 查询优化:减少快照数量,提高查询性能
- 自动执行:系统根据配置自动执行合并
- 增量保留:合并过程保留增量特性
3.3 快照生成时机
快照生成时机可以形象地理解为"水位触发"机制:
write-buffer-size (64MB)
^
| * 触发快照创建
| /
| /
| /
| /
| /
| /
| * 开始监控缓冲区
| /
| /
| /
| /
| /
| /
+-----+-----------------> 写入数据量
0 spill-threshold(0.8)
快照生成的主要时机:
- 缓冲区满触发:写入缓冲区达到
snapshot.spill-threshold阈值 - 显式触发:通过 Flink 的 Checkpoint 机制触发
- 手动触发:通过 API 或 SQL 命令手动创建快照
- 定时触发:根据系统配置定时创建
4. 快照操作详解
4.1 手动创建快照
SQL 方式创建快照
-- 使用 CALL 语句手动创建快照
CALL sys.create_snapshot('database_name.table_name');
Java API 创建快照
import org.apache.paimon.table.Table;
import org.apache.paimon.table.sink.BatchWrite;
Table table = ...; // 获取表实例
// 创建新快照
long newSnapshotId = table.newWriteBuilder().commit();
4.2 查看快照信息
SQL 方式查看
-- 查看表的所有快照信息
SELECT * FROM my_table$metadata.snapshots;
-- 查看最新的快照
SELECT * FROM my_table$metadata.snapshots ORDER BY snapshot_id DESC LIMIT 1;
Flink SQL 客户端查看
-- 列出表的所有快照
Flink SQL> DESCRIBE my_table$metadata.snapshots;
Flink SQL> SELECT * FROM my_table$metadata.snapshots;
Java API 查看
import org.apache.paimon.table.Table;
import org.apache.paimon.table.sink.BatchWrite;
Table table = ...; // 获取表实例
// 列出所有快照
List<Long> snapshotIds = table.snapshotManager().snapshotIds();
System.out.println("Available snapshots: " + snapshotIds);
4.3 读取特定快照数据
SQL 方式读取
-- 读取特定快照数据
SELECT * FROM my_table /*+ OPTIONS('scan.snapshot-id'='10') */;
-- 读取最早可用快照数据
SELECT * FROM my_table /*+ OPTIONS('scan.snapshot-id'='earliest') */;
-- 读取最新快照数据
SELECT * FROM my_table /*+ OPTIONS('scan.snapshot-id'='latest') */;
Flink SQL 读取配置
-- 创建读取特定快照的视图
CREATE VIEW my_table_snapshot_10 AS
SELECT * FROM my_table /*+ OPTIONS('scan.snapshot-id'='10') */;
-- 查询该视图
SELECT * FROM my_table_snapshot_10;
4.4 手动合并快照
手动合并快照是通过调用 API 或执行特定命令来将多个快照合并为一个,以优化存储空间和查询性能。
手动合并快照的参数
| 参数名 | 类型 | 说明 | 默认值 | 必需 |
|---|---|---|---|---|
fromSnapshotId | Long | 合并的起始快照ID | - | 是 |
toSnapshotId | Long | 合并的结束快照ID | - | 是 |
newSnapshotId | Long | 新合并快照的ID | 自动生成 | 否 |
compactLevel | String | 合并级别 (full/incremental) | incremental | 否 |
tablePath | String | 表路径 | - | 是 |
Java API 合并快照
import org.apache.paimon.catalog.Catalog;
import org.apache.paimon.catalog.Identifier;
import org.apache.paimon.table.Table;
// 获取表实例
Table table = ...;
// 合并快照 (从快照10到快照20)
long fromSnapshotId = 10L;
long toSnapshotId = 20L;
table.snapshotManager().mergeSnapshots(fromSnapshotId, toSnapshotId);
System.out.println("Snapshots merged successfully!");
Flink SQL Client 合并快照
-- 使用 CALL 语句合并快照
CALL sys.merge_snapshots(
'my_database',
'my_table',
10, -- fromSnapshotId
20 -- toSnapshotId
);
手动合并快照的最佳实践
- 定期合并:在业务低峰期定期合并历史快照
- 合理范围:不要一次合并过多快照(建议10-20个)
- 监控影响:合并过程会消耗资源,需监控系统性能
- 备份验证:合并后验证数据完整性
4.5 删除快照
SQL 方式删除
-- 使用 CALL 语句删除快照
CALL sys.drop_snapshot(
'my_database',
'my_table',
5 -- snapshotId
);
Java API 删除
import org.apache.paimon.table.Table;
// 获取表实例
Table table = ...;
// 删除特定快照
long snapshotIdToDelete = 5L;
table.snapshotManager().deleteSnapshot(snapshotIdToDelete);
System.out.println("Snapshot deleted successfully!");
5. 快照配置修改方法
5.1 创建表时配置快照
在创建表时,可以通过 WITH 子句配置快照相关参数:
CREATE TABLE my_table (
id INT,
name STRING
) WITH (
'connector' = 'paimon',
'path' = 'hdfs:///path/to/table',
'snapshot.time-retained' = '30d',
'snapshot.num-retained.min' = '5',
'snapshot.num-retained.max' = '20',
'snapshot.retention-check-interval' = '2h'
);
5.2 修改已存在表的快照配置
可以通过 ALTER TABLE 语句修改已存在表的快照配置:
-- 修改快照保留时间和数量
ALTER TABLE my_table
SET (
'snapshot.time-retained' = '14d',
'snapshot.num-retained.max' = '50'
);
-- 增加快照合并目标大小
ALTER TABLE my_table
SET ('snapshot.combine-target-size' = '256MB');
5.3 通过配置文件修改默认快照设置
在 Flink 配置文件 flink-conf.yaml 中添加以下配置,可以修改全局默认的快照行为:
# Paimon 快照默认配置
paimon.snapshot.time-retained: 7d
paimon.snapshot.num-retained.min: 1
paimon.snapshot.num-retained.max: 100
6. 快照性能优化
6.1 快照存储优化
-
合理设置保留策略:
- 生产环境建议保留14-30天的快照
- 根据数据量和存储空间调整最大保留数量
-
快照合并优化:
- 设置适当的
snapshot.combine-target-size(128MB-512MB) - 定期手动合并历史快照
- 设置适当的
-
存储格式选择:
- 使用压缩格式存储数据(如 Snappy、Zstandard)
- 配置示例:
'compression' = 'zstd'
6.2 查询性能优化
-
增量查询优化:
- 使用时间范围过滤:
WHERE proc_time BETWEEN timestamp1 AND timestamp2 - 指定快照ID:
/*+ OPTIONS('scan.snapshot-id'='10') */
- 使用时间范围过滤:
-
并行度调整:
- 根据集群资源调整查询并行度
- 配置示例:
SET execution.parallelism=16;
-
内存配置优化:
- 增加任务管理器内存:
taskmanager.memory.process.size: 4096m - 增加状态后端内存:
taskmanager.memory.managed.size: 2048m
- 增加任务管理器内存:
7. 快照常见问题与故障排除
7.1 快照过多导致性能问题
症状:
- 查询性能下降
- 元数据操作缓慢
- 存储空间快速增长
解决方案:
- 减少
snapshot.time-retained时间 - 降低
snapshot.num-retained.max数量 - 手动合并和清理旧快照:
-- 合并较旧的快照 CALL sys.merge_snapshots('my_database', 'my_table', 1, 50);
7.2 快照合并失败
症状:
- 合并操作报错
- 系统日志中出现合并相关错误
解决方案:
- 检查权限:确保有足够权限执行合并操作
- 检查资源:确保集群有足够的内存和CPU资源
- 分步合并:将大量快照分批合并,而不是一次性合并
- 重启服务:在极端情况下,重启相关服务后重试
7.3 快照删除但空间未释放
症状:
- 快照已删除但存储空间未减少
- 系统性能没有明显改善
解决方案:
- 检查是否有长时间运行的查询引用了这些快照
- 执行文件系统垃圾回收(如HDFS的expunge操作)
- 等待异步清理任务完成(通常在几小时内)
8. 参考资料
8.1 官方文档
8.2 相关链接
9. Tag功能管理详解
9.1 Tag基础概念
Tag是基于快照创建的引用,用于长期保留特定时间点的数据状态。当表配置了快照过期策略时,普通快照会被自动清理,但带有Tag的快照会被保留,其对应的数据文件也不会被删除。
Tag的主要作用:
- 长期数据保留:保留重要时间点的数据,不受快照过期策略影响
- 数据备份:为关键业务数据创建持久化备份
- 数据比对:便于在不同时间点的数据之间进行比对分析
- 合规审计:满足数据保留期限的合规要求
9.2 Tag自动创建配置
Paimon支持在写入作业中自动创建Tag,通过以下步骤配置:
9.2.1 选择创建模式
通过tag.automatic-creation参数设置创建模式,支持的值:
| 创建模式 | 说明 | 适用场景 |
|---|---|---|
| process-time | 基于机器时间创建Tag | 常规批处理和流处理场景 |
| watermark | 基于Sink输入的水印创建Tag | 事件时间处理场景 |
| batch | 在批处理场景中,任务完成后生成一个Tag | 批处理任务 |
注意:如果选择watermark模式,若水印不在UTC时区,请配置sink.watermark-time-zone参数。
9.2.2 选择创建周期
通过tag.creation-period参数设置Tag生成频率,支持的值:
- daily:每天创建
- hourly:每小时创建
- two-hours:每两小时创建
如需等待迟到数据,可配置延迟时间:tag.creation-delay(例如:'10 m’表示延迟10分钟)
9.2.3 Tag自动删除配置
通过以下参数配置Tag的自动删除策略:
tag.num-retained-max:保留的最大Tag数量tag.default-time-retained:Tag的默认保留时间
9.2.4 自动创建Tag配置示例
CREATE TABLE my_table (
id INT PRIMARY KEY NOT ENFORCED,
name STRING,
value DOUBLE
) WITH (
'connector' = 'paimon',
'path' = 'hdfs:///path/to/table',
'tag.automatic-creation' = 'process-time',
'tag.creation-period' = 'daily',
'tag.creation-delay' = '10 m',
'tag.num-retained-max' = '90'
);
9.3 手动创建Tag
Paimon支持通过多种方式手动创建Tag:
9.3.1 使用Flink SQL创建Tag
CALL sys.create_tag(`table` => 'database_name.table_name', tag => 'tag_name', [snapshot_id => <snapshot-id>]);
参数说明:
table:表名(必填)tag:Tag名称(必填)snapshot_id:基于哪个快照创建Tag,不指定则使用最新快照(可选)
9.3.2 使用Flink Action创建Tag
<FLINK_HOME>/bin/flink run \
/path/to/paimon-flink-action-1.0.1.jar \
create_tag \
--warehouse <warehouse-path> \
--database <database-name> \
--table <table-name> \
--tag_name <tag-name> \
[--snapshot <snapshot_id>] \
[--time_retained <time-retained>] \
[--catalog_conf <paimon-catalog-conf> [--catalog_conf <paimon-catalog-conf> ...]]
参数说明:
--warehouse:数据仓库路径(必填)--database:数据库名称(必填)--table:表名(必填)--tag_name:Tag名称(必填)--snapshot:快照ID,不指定则使用最新快照(可选)--time_retained:Tag的保留时间(可选)--catalog_conf:目录配置项(可选,可多个)
9.3.3 使用Java API创建Tag
import org.apache.paimon.table.Table;
// 获取表实例
table.createTag("my-tag", 1); // 基于快照ID 1 创建名为"my-tag"的Tag
table.createTag("my-tag-retained-12-hours", 1, Duration.ofHours(12)); // 指定保留12小时
9.3.4 使用Spark SQL创建Tag
-- 创建基于指定快照的Tag
CALL sys.create_tag(table => 'test.t', tag => 'test_tag', snapshot => 2);
-- 创建保留1天的Tag
CALL sys.create_tag(table => 'test.t', tag => 'test_tag', snapshot => 2, time_retained => '1 d');
-- 基于最新快照创建Tag
CALL sys.create_tag(table => 'test.t', tag => 'test_tag');
9.4 删除Tag
9.4.1 使用Flink SQL删除Tag
CALL sys.delete_tag(`table` => 'database_name.table_name', tag => 'tag_name');
参数说明:
table:表名(必填)tag:要删除的Tag名称(必填)
9.4.2 使用Flink Action删除Tag
<FLINK_HOME>/bin/flink run \
/path/to/paimon-flink-action-1.0.1.jar \
delete_tag \
--warehouse <warehouse-path> \
--database <database-name> \
--table <table-name> \
--tag_name <tag-name> \
[--catalog_conf <paimon-catalog-conf> [--catalog_conf <paimon-catalog-conf> ...]]
参数说明:
--warehouse:数据仓库路径(必填)--database:数据库名称(必填)--table:表名(必填)--tag_name:要删除的Tag名称(必填)--catalog_conf:目录配置项(可选,可多个)
9.4.3 使用Java API删除Tag
import org.apache.paimon.table.Table;
// 获取表实例
table.deleteTag("my-tag"); // 删除名为"my-tag"的Tag
9.4.4 使用Spark SQL删除Tag
CALL sys.delete_tag(table => 'test.t', tag => 'test_tag');
9.5 回滚到Tag
将表回滚到指定Tag的状态,所有快照ID大于该Tag的快照和Tag都将被删除(对应的数据也会被删除)。
9.5.1 使用Flink SQL回滚到Tag
CALL sys.rollback_to(`table` => 'database_name.table_name', tag => 'tag_name');
参数说明:
table:表名(必填)tag:要回滚到的Tag名称(必填)
9.5.2 使用Flink Action回滚到Tag
<FLINK_HOME>/bin/flink run \
/path/to/paimon-flink-action-1.0.1.jar \
rollback_to \
--warehouse <warehouse-path> \
--database <database-name> \
--table <table-name> \
--version <tag-name> \
[--catalog_conf <paimon-catalog-conf> [--catalog_conf <paimon-catalog-conf> ...]]
参数说明:
--warehouse:数据仓库路径(必填)--database:数据库名称(必填)--table:表名(必填)--version:要回滚到的Tag名称(必填)--catalog_conf:目录配置项(可选,可多个)
9.5.3 使用Java API回滚到Tag
import org.apache.paimon.table.Table;
// 获取表实例
table.rollbackTo("my-tag"); // 回滚到名为"my-tag"的Tag状态
10. 快照过期机制详解
10.1 快照过期基础
快照过期是Paimon自动管理存储空间的核心机制,通过删除不再需要的旧快照及其关联的数据文件来优化存储空间使用。
过期机制原理:
- 系统根据配置的策略定期检查快照是否过期
- 过期的快照会被标记为"待删除"
- 实际删除操作会移除快照元数据和不再被任何快照引用的数据文件
- 被Tag引用的快照不会被删除
10.2 快照过期参数详细说明
| 参数名 | 类型 | 默认值 | 说明 | 示例 |
|---|---|---|---|---|
snapshot.time-retained | Duration | 7d | 快照保留的时间,超过该时间的快照将被删除 | 14d (保留14天) |
snapshot.num-retained.min | Integer | 1 | 保留的最小快照数量,即使超过时间也不会删除 | 3 (至少保留3个快照) |
snapshot.num-retained.max | Integer | 100 | 保留的最大快照数量,即使未超过时间也会删除最旧的 | 50 (最多保留50个快照) |
snapshot.retention-check-interval | Duration | 1h | 检查快照过期的时间间隔 | 30m (每30分钟检查一次) |
10.3 快照过期执行方式
10.3.1 自动过期
Paimon会在以下时机自动执行快照过期检查:
- 新快照创建时
- 达到配置的
snapshot.retention-check-interval时间间隔 - 表操作(如合并、删除等)执行后
自动过期由系统后台自动处理,无需手动干预。
10.3.2 手动触发过期
在Flink SQL中,可以通过存储过程手动触发快照过期:
CALL sys.expire_snapshots('database_name.table_name');
10.3.3 使用Flink Action触发过期
对于不支持存储过程的Flink版本(如Flink 1.17),可以使用Flink Action手动触发快照过期:
<FLINK_HOME>/bin/flink run \
/path/to/paimon-flink-action-1.0.1.jar \
expire_snapshots \
--warehouse <warehouse-path> \
--database <database-name> \
--table <table-name> \
[--catalog_conf <paimon-catalog-conf> [--catalog_conf <paimon-catalog-conf> ...]]
参数说明:
--warehouse:数据仓库路径(必填)--database:数据库名称(必填)--table:表名(必填)--catalog_conf:目录配置项(可选,可多个)
10.4 快照过期与Tag的关系
快照过期机制与Tag管理密切相关:
- 被任何Tag引用的快照不会被删除,即使它已经超过了保留时间
- 删除Tag后,其引用的快照会在下一次过期检查中根据过期策略决定是否删除
- 合理设置Tag保留策略可以避免重要数据被意外清理
10.5 快照过期最佳实践
10.5.1 配置推荐
根据不同场景,推荐以下配置策略:
实时处理场景:
snapshot.time-retained:24h(保留1天)snapshot.num-retained.min:5(至少保留5个快照)snapshot.num-retained.max:24(最多保留24个快照)snapshot.retention-check-interval:1h(每小时检查一次)
批处理场景:
snapshot.time-retained:7d(保留7天)snapshot.num-retained.min:3(至少保留3个快照)snapshot.num-retained.max:50(最多保留50个快照)snapshot.retention-check-interval:6h(每6小时检查一次)
重要数据场景:
snapshot.time-retained:30d(保留30天)snapshot.num-retained.min:10(至少保留10个快照)snapshot.num-retained.max:100(最多保留100个快照)- 结合Tag使用,为关键时间点创建长期保留的Tag
10.5.2 监控与调优
- 监控快照数量:定期检查表的快照数量,避免快照过多导致查询性能下降
- 监控存储空间:关注表的存储增长趋势,及时调整过期策略
- 性能调优:对于快照数量较大的表,考虑增加
snapshot.combine-target-size参数值,促进快照合并
通过合理配置和监控,可以在数据保留和存储空间之间取得最佳平衡。
11. 附录:快照管理命令速查
11.1 SQL 命令速查
-- 创建表并配置快照
CREATE TABLE my_table (...) WITH ('snapshot.time-retained' = '30d', ...);
-- 修改快照配置
ALTER TABLE my_table SET ('snapshot.num-retained.max' = '50');
-- 查看快照信息
SELECT * FROM my_table$metadata.snapshots;
-- 读取特定快照数据
SELECT * FROM my_table /*+ OPTIONS('scan.snapshot-id'='10') */;
-- 合并快照
CALL sys.merge_snapshots('db', 'table', 1, 10);
-- 删除快照
CALL sys.drop_snapshot('db', 'table', 5);
11.2 Java API 速查
// 获取表实例
Table table = ...;
// 查看快照
List<Long> snapshotIds = table.snapshotManager().snapshotIds();
// 合并快照
table.snapshotManager().mergeSnapshots(1, 10);
// 删除快照
table.snapshotManager().deleteSnapshot(5);
// 创建新快照
long newSnapshotId = table.newWriteBuilder().withCommitMessage(...).commit();
3904

被折叠的 条评论
为什么被折叠?



