第13章:分区与过期管理
导言:如何优雅地清理旧数据
在运营一个大型数据系统时,会积累大量的历史数据。如何高效地清理?如何避免误删? 这就是分区与过期管理的核心问题。
第一部分:分区设计
1.1 分区的作用
css
无分区表:
Table/
└── bucket-0/
├── file_1.parquet
├── file_2.parquet
└── ... (万级文件)
问题:查询某日数据需要扫描所有文件
有分区表:
Table/
├── dt=2024-01-01/
│ └── bucket-0/
│ ├── file_1.parquet
│ ├── file_2.parquet
│ └── ...
├── dt=2024-01-02/
│ └── bucket-0/
│ ├── file_3.parquet
│ └── ...
└── ...
优势:
├─ 查询特定日期数据,仅扫描一个分区
├─ 清理历史数据,直接删除整个分区目录
└─ 性能提升100倍
1.2 分区字段选择
sql
推荐:按时间分区
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
user_id BIGINT,
amount DECIMAL,
dt DATE
) PARTITIONED BY (dt) ← 按日期分区
原因:
├─ 时间维度天然递进
├─ 容易按日期删除旧数据
└─ 符合数据生命周期
多级分区(可选):
CREATE TABLE events (
event_id STRING PRIMARY KEY,
...
dt DATE,
hour INT
) PARTITIONED BY (dt, hour) ← 年月日 + 小时
优势:
├─ 更精细的数据组织
├─ 支持按小时清理
└─ 但增加复杂性(文件数增加)
第二部分:分区过期策略
2.1 自动过期配置
yaml
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
...
dt DATE
) PARTITIONED BY (dt) WITH (
'partition.expiration.time' = '30d',
'partition.expiration.check.interval' = '1d',
'partition.expiration.strategy' = 'drop'
);
配置说明:
├─ partition.expiration.time:保留30天数据
├─ partition.expiration.check.interval:每天检查一次
└─ partition.expiration.strategy:直接删除分区
执行流程:
Time 1: Snapshot提交时检查是否有过期分区
Time 2: 发现dt=2023-12-01的分区(30天前)
Time 3: 删除该分区的所有文件
└─ 自动进行,无需手动干预
2.2 分区过期策略
sql
策略1:DROP(删除)
├─ 直接删除过期分区
├─ 最常用
└─ 无法恢复
策略2:ARCHIVE(归档)
├─ 将过期分区移到低温存储
├─ 支持后续恢复
└─ 成本优化(冷热数据分离)
策略3:SOFT_DELETE(软删除)
├─ 标记为已删除,但不真正删除
├─ 支持快速恢复
└─ 适合关键数据
第三部分:动态分区重写
3.1 什么是动态分区重写
某些写入会涉及多个分区,但无法提前知道目标分区。此时使用动态分区重写:
ini
Upsert场景:
User A初始化:dt=2024-01-01, data="initial"
User A 30天后更新:dt=2024-01-31, data="updated"
同一主键跨越不同分区!
动态分区重写的作用:
├─ 接受跨分区的更新
├─ 自动路由到正确分区
├─ 保持数据一致性
└─ 支持ACID事务
配置:
'dynamic-partition-overwrite' = 'true'
第四部分:跨分区更新
4.1 跨分区UPDATE
sql
-- 场景:用户状态从一个月跨到下一个月
UPDATE orders SET status='completed'
WHERE user_id=123 AND dt >= '2024-01-01';
目标行可能分散在多个分区:
├─ dt=2024-01-01的order(旧)
├─ dt=2024-01-15的order(新)
└─ ...
Paimon需要:
├─ 读取所有相关分区
├─ 应用更新
├─ 写入回各自的分区
└─ 提交一个原子Snapshot
4.2 跨分区DELETE
sql
-- 场景:删除用户所有订单
DELETE FROM orders WHERE user_id=123;
受影响的分区:所有存在该用户的分区
Paimon处理:
├─ 找出所有包含user_id=123的分区
├─ 从各分区删除该用户的记录
├─ 生成新的Snapshot
└─ 原子提交
第五部分:生产级配置
5.1 电商订单表
yaml
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
user_id BIGINT,
amount DECIMAL,
status STRING,
created_at BIGINT,
updated_at BIGINT,
dt DATE
) PARTITIONED BY (dt) WITH (
'bucket' = '16',
'merge-engine' = 'deduplicate',
'sequence.field' = 'updated_at',
'partition.expiration.time' = '90d',
'partition.expiration.check.interval' = '1d',
'dynamic-partition-overwrite' = 'true',
'dynamic-partition-path' = '/path/to/default'
);
配置说明:
├─ 按日期分区,90天过期策略
├─ 支持跨分区更新
├─ 自动清理3个月前数据
└─ 存储成本:日均100GB增长,3个月清理可节省9TB
5.2 实时日志表
yaml
CREATE TABLE logs (
log_id STRING,
level STRING,
message STRING,
ts BIGINT,
dt DATE,
hour INT
) PARTITIONED BY (dt, hour) WITH (
'bucket' = '-1',
'partition.expiration.time' = '7d',
'partition.expiration.check.interval' = '1h'
);
配置说明:
├─ 按日期和小时二级分区
├─ 仅保留7天日志
├─ 每小时检查过期
└─ 及时清理,成本最优
总结
分区的核心价值
效率:
└─ 查询特定分区时,避免扫描全表
成本:
├─ 自动清理旧分区
└─ 按时间维度管理数据生命周期
可维护性:
└─ 清晰的数据组织结构
配置checklist
- 选择合适的分区字段(通常是日期)
- 根据业务需求设置过期时间
- 配置检查间隔(多久检查一次过期分区)
- 启用动态分区(如需要跨分区更新)
- 定期验证过期策略是否正确执行
下一章:第14章讲解Tag与分支管理