流式数据湖Paimon探秘之旅 (十一) Changelog变更日志

第11章:Changelog变更日志

导言:实时数据流的核心

在第10章,我们讲了同一主键的多个版本如何合并 。现在需要讨论如何让下游系统感知到这些变化 。答案就是Changelog(变更日志)

Changelog的作用

ini 复制代码
原始表数据:
order_id=1: status="未支付" → "已支付" → "已发货" → "已签收"

Changelog记录:
{order_id=1, status="未支付", _change_flag=INSERT}
{order_id=1, status="已支付", _change_flag=UPDATE_AFTER}
{order_id=1, status="已发货", _change_flag=UPDATE_AFTER}
{order_id=1, status="已签收", _change_flag=UPDATE_AFTER}

下游系统(如Kafka、实时处理)可以实时感知这些变化

第一部分:Changelog的基本概念

1.1 Change Flag(变化标记)

每条Changelog记录都有一个Change Flag,表示数据的变化类型:

sql 复制代码
+I (INSERT):新增数据
-D (DELETE):删除数据
-U (UPDATE_BEFORE):更新前的镜像(用于旧值)
+U (UPDATE_AFTER):更新后的值(用于新值)

例子:
订单状态从"未支付"更新为"已支付"

Changelog:
-U: {order_id=1, status="未支付"}  ← 旧值
+U: {order_id=1, status="已支付"}  ← 新值

下游系统可以:
├─ 看到完整的前后对比
├─ 识别出是UPDATE操作
└─ 可以决定如何处理(仅保留新值、两者都保留等)

1.2 ChangelogProducer(变更日志生成器)

ChangelogProducer负责生成和维护Changelog

sql 复制代码
配置:
changelog-producer: NONE | INPUT | FULL_COMPACTION | LOOKUP

NONE:不生成Changelog
INPUT:仅从输入生成(只有INSERT/DELETE)
FULL_COMPACTION:全量压缩时生成(完整的前后镜像)
LOOKUP:使用Lookup表加速(实时生成)

第二部分:Changelog的生成模式

2.1 NONE模式

yaml 复制代码
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY,
    status STRING,
    ...
) WITH (
    'changelog-producer' = 'NONE'
);

特点:
├─ 不生成Changelog
├─ 性能最优(减少开销)
└─ 下游看不到数据变化

使用场景:
└─ 不需要实时感知变化的场景
   如纯分析表、非实时处理

2.2 INPUT模式

yaml 复制代码
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY,
    status STRING,
    ...
) WITH (
    'changelog-producer' = 'INPUT'
);

生成的Changelog:
只包含来自应用的INSERT和DELETE
不包含更新操作的前值

例子:
应用插入:INSERT order_id=1, status="未支付"
  → Changelog: +I {order_id=1, status="未支付"}

应用删除:DELETE order_id=1
  → Changelog: -D {order_id=1, status="未支付"}

应用更新:UPDATE status="已支付" WHERE order_id=1
  → Changelog: +I {order_id=1, status="已支付"} (新值)
           -D {order_id=1, status="未支付"} (旧值)

特点:
├─ 生成完整的前后镜像
├─ 开销中等
└─ 适合需要UPDATE前值的场景

2.3 FULL_COMPACTION模式

yaml 复制代码
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY,
    status STRING,
    ...
) WITH (
    'changelog-producer' = 'FULL_COMPACTION'
);

生成的Changelog:
仅在Compaction完成时生成
包含完整的前后镜像

流程:
Time 1: 用户插入order_id=1, status="未支付"
  → 内部写入Level 0,暂不生成Changelog

Time 2: 用户更新status="已支付"
  → 内部写入Level 0,暂不生成Changelog

Time 3: Compaction触发(Level 0→Level 1)
  → Changelog生成!
     -D {order_id=1, status="未支付"}
     +U {order_id=1, status="已支付"}

特点:
├─ Changelog生成延迟(等待Compaction)
├─ 性能最优(批量生成)
└─ 适合可接受短延迟的场景

2.4 LOOKUP模式

yaml 复制代码
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY,
    status STRING,
    ...
) WITH (
    'changelog-producer' = 'LOOKUP'
);

生成的Changelog:
实时生成,每次写入时立即生成

流程:
Time 1: 用户插入order_id=1, status="未支付"
  → 立即生成Changelog: +I {order_id=1, status="未支付"}

Time 2: 用户更新status="已支付"
  → Lookup旧值(从现有数据读取)
  → 立即生成Changelog:
     -U {order_id=1, status="未支付"}  (旧值来自Lookup)
     +U {order_id=1, status="已支付"}  (新值来自输入)

特点:
├─ 实时生成,无延迟
├─ 开销较大(每次Lookup)
└─ 适合实时流处理

第三部分:Changelog的消费

3.1 从Paimon读取Changelog

java 复制代码
// 创建支持Changelog的表扫描
StreamDataTableScan scan = table
    .newStreamScan()
    .withChangelogMode(ChangelogMode.ALL);  // 读取所有变化

// 扫描Changelog
DataIterator iterator = scan.plan().execute();
while (iterator.hasNext()) {
    Row row = iterator.next();
    
    // 获取变化标记
    RowKind kind = row.getKind();
    
    switch (kind) {
        case INSERT:
            System.out.println("新增:" + row);
            break;
        case UPDATE_BEFORE:
            System.out.println("更新前:" + row);
            break;
        case UPDATE_AFTER:
            System.out.println("更新后:" + row);
            break;
        case DELETE:
            System.out.println("删除:" + row);
            break;
    }
}

3.2 Flink消费Changelog

java 复制代码
StreamExecutionEnvironment env = 
    StreamExecutionEnvironment.getExecutionEnvironment();

TableEnvironment tableEnv = StreamTableEnvironment.create(env);

// 注册Paimon表
tableEnv.executeSql(
    "CREATE TABLE orders ("
    + "  order_id BIGINT,"
    + "  status STRING,"
    + "  PRIMARY KEY (order_id) NOT ENFORCED"
    + ") WITH ("
    + "  'connector' = 'paimon',"
    + "  'path' = '/path/to/paimon/orders'"
    + ")");

// 实时读取Changelog
DataStream<Row> stream = tableEnv.toChangelogStream(
    tableEnv.sqlQuery("SELECT * FROM orders"));

stream.print();

env.execute();

3.3 Kafka同步

Paimon可以将Changelog实时同步到Kafka:

yaml 复制代码
CREATE TABLE orders_kafka (
    order_id BIGINT,
    status STRING,
    ...
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'connector' = 'kafka',
    'topic' = 'orders-changelog',
    'properties.bootstrap.servers' = 'localhost:9092',
    'format' = 'avro'
);

-- 同步Changelog到Kafka
INSERT INTO orders_kafka
SELECT * FROM orders WHERE __op <> 'before';

第四部分:生产级配置

4.1 实时数据同步场景

yaml 复制代码
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY,
    user_id BIGINT,
    amount DECIMAL,
    status STRING,
    created_at BIGINT,
    updated_at BIGINT,
    ...
) WITH (
    'changelog-producer' = 'LOOKUP',
    'changelog.num.retained.min' = '5',
    'changelog.num.retained.max' = '10'
);

-- 配置说明:
-- changelog-producer:使用LOOKUP实时生成
-- changelog.num.retained.min/max:最少保留5个,最多10个Snapshot的Changelog

下游消费:
Flink CDC Source → Paimon Table
    ↓
Changelog生成
    ↓
Kafka Sink(同步给消费方)

4.2 低延迟分析场景

yaml 复制代码
CREATE TABLE metrics (
    hour STRING PRIMARY KEY,
    page_id STRING PRIMARY KEY,
    pv BIGINT,
    uv BIGINT,
    ...
) WITH (
    'changelog-producer' = 'FULL_COMPACTION',
    'compaction.min.file-num' = '3',
    'num-sorted-run-compaction-trigger' = '3'
);

-- 配置说明:
-- 激进的Compaction触发,使得Changelog定期生成
-- 下游可以定时拉取Changelog进行分析

第五部分:Changelog的局限与最佳实践

5.1 选择合适的模式

css 复制代码
场景1:实时数据同步(CDC)
→ 使用LOOKUP
└─ 需要实时前后镜像

场景2:准实时分析(小时级)
→ 使用FULL_COMPACTION
└─ 可接受Compaction延迟

场景3:纯仓库(无下游实时处理)
→ 使用NONE
└─ 最优性能

5.2 开销管理

arduino 复制代码
LOOKUP模式开销:
├─ 每条记录都要Lookup旧值
├─ 额外的读取IO:~2倍
└─ 若表频繁更新,开销严重

FULL_COMPACTION模式开销:
├─ Changelog生成不涉及额外IO
├─ 仅在Compaction时生成
└─ 总体开销低,但有延迟

建议:
├─ 小表(<1GB)→ LOOKUP(可接受额外开销)
├─ 中表(1-100GB)→ FULL_COMPACTION(平衡方案)
└─ 大表(>100GB)→ 考虑不启用Changelog或使用INPUT

总结

Changelog的核心价值

markdown 复制代码
Paimon表
    ↓ (启用Changelog)
原始数据 + Changelog数据流
    ↓
下游系统可以:
├─ 实时感知数据变化
├─ 获取完整的前后镜像
├─ 同步到其他系统(Kafka、数据库等)
└─ 支持CDC(Change Data Capture)

配置checklist

  • 根据业务选择changelog-producer模式
  • 设置合理的changelog retention(保留时间)
  • 如果性能敏感,评估开销
  • 配置下游消费者的处理逻辑

下一章预告:第12章讲解索引与加速,包括Deletion Vector、文件索引、统计信息

相关推荐
语落心生32 分钟前
流式数据湖Paimon探秘之旅 (十六) Flink集成深度解析
大数据
数据与后端架构提升之路36 分钟前
自动驾驶仿真数据闭环:如何利用大数据构建“上帝视角”的虚拟矩阵?(硬核指南)
大数据·矩阵·自动驾驶
TDengine (老段)36 分钟前
TDengine 时区函数 TIMEZONE 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
语落心生37 分钟前
流式数据湖Paimon探秘之旅 (五) 写入流程全解析
大数据
语落心生38 分钟前
流式数据湖Paimon探秘之旅 (九) Compaction压缩机制
大数据
语落心生39 分钟前
流式数据湖Paimon探秘之旅 (十) Merge Engine合并引擎
大数据
en-route39 分钟前
深入理解数据仓库设计:事实表与事实宽表的区别与应用
大数据·数据仓库·spark
语落心生40 分钟前
流式数据湖Paimon探秘之旅 (八) LSM Tree核心原理
大数据
Light6042 分钟前
智慧办公新纪元:领码SPARK融合平台如何重塑企业OA核心价值
大数据·spark·oa系统·apaas·智能办公·领码spark·流程再造