01沿数据流向阅读

持续路径是:合成业务流量 → MySQL → Flink CDC → Fluss 源表 → Flink 常规 Join。Join 结果分别写入 Fluss 宽表、独立 Iceberg 表和带 key 的 Kafka topic,再由 Kafka Engine 表和物化视图写入 ClickHouse。

Fluss 将宽表分层到 Paimon。有界 Flink 作业读取 Fluss/Paimon 的统一表,计算小时汇总,并将其行数与 Iceberg 比较。批任务的结果也经 Kafka 进入 ClickHouse。

02各层分别负责什么?

层实现职责
业务源MySQL retail 表客户、商品、订单及每单一笔支付;输出 Row Binlog 变更
变更捕获Flink CDC 作业 01初始快照与增量变更写入主键表
流存储Fluss retail 表维护按 key 更新的当前记录,并向下游提供变更
持续计算Flink SQL 作业 02通过常规 Left Join 持续维护订单宽表
分发与查询Kafka → ClickHouse按 key 传输结果、物化写入和分析查询
湖存储Paimon 与 IcebergPaimon 是 Fluss 分层存储;Iceberg 是独立的 v2 upsert sink
有界计算Flink SQL 作业 10、11小时汇总和最小化的跨湖表行数对账
支撑组件RustFS、ZooKeeper、本地控制台对象存储、Fluss 协调与本地检查

03区分三种数据形态

  • 业务表回答“现在是什么状态”,Binlog 记录数据库行如何变化。
  • 带 key 的变更流可能包含同一订单的多个版本;消息条数不等于订单数。
  • 湖表查询读取已提交状态,不同 sink 暴露同一次变更的时间可能不同。

04Exactly-once 有明确的边界

源码配置了 Flink checkpoint,并为实时 Kafka sink 配置事务投递。仅有这些配置,不能证明整条链路不丢不重;还需检查恢复过程、消费者隔离级别、sink 提交行为和最终查询。

ClickHouse 用 ReplacingMergeTree(version) 保存多个版本,看板查询使用 FINAL 去重。当前 Kafka 入库链路不会把 tombstone 转成 ClickHouse 行删除。源端删除、服务层保留和湖表快照清理是不同的问题。

对账比较的是分别读取的两张表的行数。相同行数可能隐藏字段变化或 key 集合差异;非零差值也可能来自不同的提交进度。

05当前实现的存储与时间选择

Paimon 通过 Fluss tiering 使用本地 S3 兼容对象存储。Iceberg 使用共享 Docker 数据卷上的 Hadoop catalog。因此项目展示的是两条湖表路径,并未展示 Iceberg S3 存储或生产级共享 catalog。

Join 是配置了七天状态 TTL 的动态表常规 Join,没有定义 watermark、事件时间窗口或迟到事件侧输出。TTL 限制空闲状态的保留,状态到期后再到达的更新可能得到不同结果。

多数 SQL 和 ClickHouse 时间字段使用 Asia/Shanghai。这是实验声明的业务时区;只修改容器 TZ 变量,并不能迁移全部查询语义。

06在源码中找到对应契约

问题源码文件
流量如何产生?apps/mock/main.py
哪些数据进入 CDC?sql/01_cdc_to_fluss.sql
订单如何关联与分发?sql/02_wide_pipeline.sql
批任务计算什么?sql/10_batch_aggregate.sql
对账实际比较什么?sql/11_lake_audit.sql
ClickHouse 如何保存版本?clickhouse/init/001_analytics.sql
运行哪些镜像和持久卷?compose.yml