作者:费红健
本文整理自 Apache 2026 技术分享《实时湖仓管道:分离式 Kafka 中的原生入湖与内嵌计算》。
存算分离 Kafka 带来的变化,不只是 Broker 变得更轻、存储可以独立扩展,以及容量与计算能够分别弹性伸缩。

图 1:Kafka 协议保持不变,Broker 计算层与远端 Segment 存储层解耦,二者可以独立扩缩容。
更重要的是,Kafka 的标准协议与实时数据面得以保留,而消息的长期持久化不再紧密绑定 Broker 本地磁盘。当数据底座完成存算解耦,开放表格式、Catalog 与托管运行时也逐渐成熟,同一份 Topic 数据便可以原生走向两条不同的价值路径:
- 一条是 Table:把持续到达的消息沉淀为 Iceberg 开放表,让实时数据成为可以被多种引擎长期复用的资产。
- 另一条是 SQL:让消息到达后立即参与过滤、窗口、聚合与路由,持续产生面向业务的实时结果。
但"原生"并不意味着处理过程被省略。Table 侧仍然要处理记录映射、Schema 演进、CDC、原子提交、Exactly-once 与小文件治理;SQL 侧也需要独立的运行时、资源隔离、故障恢复和任务级观测。真正发生变化的,是这些能力由谁长期承担,以及用户需要面对多大的外部责任边界。
本文以云消息队列 Kafka 版的 Topic 消息入湖与内置流计算为例,回答三个问题:
-
存算分离为什么会成为原生 Table 入湖与实时 SQL 计算的共同底座?
-
一批 Kafka 消息如何经过 Schema、CDC、原子提交与故障恢复,最终成为可信的开放表版本?
-
Table 与 SQL 各自解决什么问题,又应该如何划分能力与运行时边界?
存算分离之后:一份数据,两条价值路径
在很多实时业务里,同一条事件会同时拥有两种价值:
- 第一种价值发生在当下。例如,订单创建后立刻更新指标,风控事件到达后立即判断,日志进入 Kafka 后实时聚合并触发告警。它强调低延迟、持续计算和快速产出结果。
- 第二种价值沉淀在未来。数据需要进入开放表,用于离线分析、特征工程、模型训练、历史回溯、审计与跨引擎查询。它强调开放格式、长期可读和稳定治理。
因此,平台需要提供两条彼此独立、又能共享底层数据基础的路径:
- TABLE 路径: 把消息持续转化为开放表,形成可长期复用的数据资产;
- SQL 路径: 让事件到达后立即参与过滤、窗口、聚合和路由,持续产生实时结果。

图 2:TABLE 产生开放资产,SQL 产生实时结果;两者共享存算分离的 Kafka 底座。
这不是要把两类工作塞进同一个运行时,而是给它们一个共同的数据起点,并分别设置清晰的责任边界。TABLE 关心的是"这张表能否长期正确";SQL 关心的是"这段计算能否持续、独立、可观测地运行"。
持续入湖平台化,需要三个前提
过去,入湖通常以一条独立数据管道存在:准备计算集群,部署 Flink 或 Spark,维护 Connector,再接入对象存储和 Catalog。这个方案灵活,但也意味着用户要同时维护消息系统、计算运行时、表格式和存储系统之间的关系。
要把持续入湖从"用户自己搭一条管道"变成"平台提供一种关系",至少需要三个前提。

图 3:存算分离、开放格式与 Serverless 化,让持续入湖具备平台化基础。
第一,Kafka 存储与计算解耦。 消息仍通过熟悉的 Kafka 协议写入和消费,但数据的持久化不再紧密依附单台 Broker 的本地磁盘。底层存储具备独立扩展能力后,同一份数据就更容易被组织为不同的数据形态。
第二,开放格式优先。 数据不能只落成一堆彼此孤立的文件。Iceberg 这样的开放表格式提供 Snapshot、Manifest、Schema、分区演进等表级语义;Catalog 则让多种计算引擎以一致的方式发现和读取这些表。
如果是第一次接触这些概念,可以先把它们理解为一条"表版本索引链":Catalog 负责发现表、管理命名空间,并保存表元数据的入口;表元数据指向当前 Snapshot,也就是某一时刻完整、可见的表版本;Snapshot 再通过 Manifest 组织该版本引用的数据文件、删除文件及其统计信息。DataFile 保存行数据,DeleteFile 保存哪些行应当失效。查询引擎沿着这条链读取的是一张有版本、有 Schema 的表,而不是自行解释对象存储中的一堆文件。
第三,运行时 Serverless 化。 用户希望表达的是"把这个 Topic 持续写成这张表",而不是"替我长期照看一套额外集群"。资源调度、失败恢复、扩缩容和版本维护应尽可能由平台吸收。
三个条件共同改变了入湖的形态:它不必总是一项外置计算任务,也可以成为 Kafka Topic 与开放表之间的一项托管配置。
原生入湖的本质:复杂度没有消失,只是责任换了位置
如果只看第一次写入,持续入湖很容易被理解成"消息转文件"。但一张长期运行的表至少要承担五类责任。

图 4:一条可用的入湖链路,需要同时处理文件、Schema、CDC、提交和恢复。
- 小文件治理: Kafka 的数据天然以分区、批次和时间持续到达。批次太小、分区太多或提交过于频繁,都会制造大量小文件,进而放大元数据读取、查询规划、存储请求和后台合并的成本。
- Schema 演进: 业务字段会新增,必填字段可能放宽为可选,数值精度也可能扩大。入湖链路必须判断哪些变化兼容、如何更新表结构,以及不兼容时应如何阻断和告警。
- CDC 还原: 对数据库变更日志而言,Insert、Update、Delete 不是三种普通消息,而是在描述一张表的当前状态。入湖系统需要把事件语义转化为 DataFile 与 DeleteFile,并在读取时还原一致视图。
- 原子提交: 文件上传成功,不等于新版本已经生效。只有当一组数据文件、删除文件和元数据作为同一个 Snapshot 原子提交后,下游才能看到一个完整的新版本。
- 失败恢复: 任务可能在写文件之后、提交之前宕机,也可能在进度推进的边界失败。恢复机制既要允许安全重放,又要保证同一批数据不会在逻辑上重复生效。
这五件事不会因为采用原生能力而消失。变化的只是:它们从用户可见的外部任务,进入平台内部被统一承担。
1. 三种路径,三种责任划分
围绕 Kafka 入湖,常见方案大致可以分为三类。

图 5:Connector、生态计算平台和原生入湖都能完成任务,核心差异是运行时与表级责任的归属;代表产品与能力状态以发布时信息为准。
- Connector 路径接近消息系统,部署直接,适合已有 Connect 体系、转换逻辑相对明确的团队。用户通常仍需关注 Connector 集群、任务并发、版本兼容和表提交行为。
- 生态计算平台路径最灵活。Flink、Spark 等引擎可以承载复杂清洗、关联、状态计算和多目标写入,也拥有成熟的工程生态。相应地,用户需要运营额外运行时,并理解计算 Checkpoint、Sink 提交与表 Snapshot 之间的关系。
- 原生入湖路径面向更聚焦的需求:当任务主要是把 Kafka 数据持续、可靠地沉淀为开放表时,由平台托管格式转换、Schema 感知、事务提交、恢复和表维护,可以显著缩小用户需要照看的系统边界。
三条路径并不存在绝对优劣。复杂关联、定制算子、多路加工仍适合专业计算平台;已经标准化的持续入湖,则更适合被收敛成产品能力。选择方案时,关键问题不是"谁的链路图更短",而是"团队希望长期拥有哪部分复杂度"。
2. 从 7 个外部节点到 3 个
在本文的示意链路中,传统外置方式包含 7 个用户可见节点:Kafka、Record Mapping、Schema、File Layout、Commit、Recovery,以及 Iceberg + Object Storage。任何一处升级、抖动或配置变化,都可能跨系统传导。
在原生模式下,对外呈现为 3 个用户可见节点:Kafka Topic、Iceberg Table 和 Object Storage。Record Mapping、Schema、File Layout、Commit 与 Recovery 仍然存在,只是被收进平台内部。

图 6:示意链路中,用户可见节点由 7 个收敛为 3 个;内部仍完整执行映射、Schema、布局、提交与恢复。
以当前产品术语来说,用户可以在原有 Topic 上启用消息入湖配置,将数据持续写入 OSS Table Bucket 中的 Iceberg 表 。它不是复制出一个新的 Kafka Topic,而是在原数据源上建立一条托管的表关系。
这种变化最重要的价值不是"少画了几个框",而是故障域变小:
- 不需要为单一入湖目的单独维护一套外部计算集群;
- 不需要在多个控制面之间反复同步任务状态;
- 表提交与消费进度可以在同一托管链路内协调;
- Schema、Compact、Snapshot 清理等能力不再由每条任务各自实现。
换句话说,原生入湖没有取消处理,而是改变了处理的所有权。
从消息到表:一个可信 Snapshot 如何诞生
把链路拉近看,一批 Kafka 消息从读取到成为开放表的一部分,通常要经过以下步骤。

图 7:消息经过映射、Schema 与 CDC 处理后形成候选文件,只有原子提交完成才成为可见 Snapshot。
首先,系统从一个或多个 Partition 读取一批确定的 offset 范围,并记录这批输入的边界。随后按照配置把消息 Key、Value、Header 或时间戳映射为表字段。
接下来 是 Schema 感知。假设业务把 amount 从 DECIMAL(10,2) 扩大为 DECIMAL(12,2),把 country 从必填放宽为可选,又新增了可选字段 channel STRING。这些变化的共同点是:旧数据仍可以被新 Schema 合法解释,因此可以在受支持的演进规则内更新表结构。反过来,如果新类型无法兼容旧数据,系统就不应静默猜测,而应阻断提交并暴露错误。

图 8:类型提升、字段可选化和新增字段的 Schema 演进示例。
然后 ,系统根据普通追加或 CDC 语义生成候选 DataFile 、DeleteFile ,并构建新的 Manifest 与 Snapshot 元数据。此时文件可能已经写入存储,但对读者仍不可见。
最后 一步才是提交:Catalog 中的 metadata location 经过条件更新,从 metadata N 切换到 metadata N+1,后者引用新的 Snapshot。更新被确认成功后,整批数据才一次性可见。若发生条件冲突,metadata location 仍指向旧版本,需要基于最新 metadata 重建提交;若提交结果未知,则必须先刷新 current metadata,确认上一次提交究竟是否成功,再决定下一步。两种情况不能混为一谈。

图 9:文件落盘不代表版本生效;Catalog 指针成功切换后,新 metadata 引用的 Snapshot 才整体可见。
这就是开放表里一个非常重要的边界:文件是物理载体,Snapshot 才是逻辑版本。
1. CDC:把事件流还原为当前视图
CDC 场景进一步说明了为什么入湖不能只做格式转换。
假设源表以主键 id 识别一行数据:
- Insert 会新增一条记录,可写入 DataFile;
- Delete 需要写入针对该主键的 Equality Delete;
- Update 可以理解为"删除旧主键对应的行,再写入新版本"。
这里的 Equality Delete,可以理解为"按一个或多个等值字段匹配的逻辑删除"。在本文讨论的实现中,它不会原地修改旧 DataFile,而是把需要失效的主键值写入 DeleteFile;读取时,引擎将 DeleteFile 与 DataFile 合并,过滤匹配的旧记录。因此,一次 Update 在表里表现为"旧值失效 + 新值写入",最终读者看到的是当前视图,而不是变更事件的简单堆叠。

图 10:Insert、Delete、Update 被转化为 DataFile 与 Equality Delete,查询时合并为一致的当前视图。
读取时,计算引擎并不是简单拼接所有 DataFile,而是结合 DeleteFile 过滤已经失效的记录,得到当前 Snapshot 对应的逻辑视图。
这里有两个容易忽略的前提。第一,CDC 必须具有稳定且可识别的主键,否则系统无法准确表达"删除的是哪一行"。第二,源端事件顺序和重复投递语义必须清晰,否则同一主键的多次更新可能被错误还原。
因此,CDC 入湖的配置表面上可能只有几个选项,背后却同时连接了消息顺序、主键语义、文件组织和表版本管理。
2. Exactly-once 不等于"物理上只写一次"
在分布式系统中,失败可能发生在任何边界。一个典型场景是:文件已经写完,Snapshot 还没提交,任务突然宕机。
恢复后,如果系统无法确认旧文件是否已经生效,最安全的选择往往是重新读取并重写这一批数据。于是物理存储中可能先后出现 File-Set-A 和 File-Set-B。

图 11:offset 100--199 可能物理写入两次,但只有与进度绑定成功的 Snapshot 42 逻辑生效一次。
图中的例子从 orders-0 的 offset 100--199 开始。初始状态是 Snapshot 41,已确认进度为 99。第一次尝试写出了 File-Set-A,却在提交前失败;故障恢复后,同一批数据被重放并生成 File-Set-B。只有当 Snapshot 42 与消费进度 199 被一致地确认后,这 100 条事件才对下游可见,下一批从 offset 200 开始。
因此,Exactly-once 的准确含义是:同一批输入即使经历重试,最终也只在表的逻辑版本中生效一次。
它并不承诺底层永远只产生一份物理文件。未被任何有效 Snapshot 引用的文件可以由后台游离文件清理机制回收。
要实现这个语义,仅有 Iceberg 的原子 Snapshot 还不够,还需要把"输入进度"与"表版本生效"协调起来。当前消息入湖文档将消费进度保存在 Kafka Leader 元数据中,并由托管链路完成故障后的续传与去重;具体能力边界仍应以发布时的产品文档为准。
写入只是开始:让表长期健康且保持开放
持续写入的表即使语义完全正确,也可能随着时间变得越来越难用。
假设数据来自多个 Kafka Partition,每个 Partition 都以很短的周期提交文件。一天以后,表中可能出现大量只有几百 KB 或几 MB 的文件。查询引擎需要打开更多文件、读取更多元数据;后台也要付出更多请求和调度成本。

图 12:小批次、多分区与频繁提交会持续制造碎片,并放大元数据、规划、合并和请求成本。
因此,长期运行还需要三类维护动作:
- Compact: 把多个小文件合并为更适合读取的大文件,并优化数据布局;
- Snapshot 过期清理: 删除超过保留策略、不再需要的历史版本引用;
- Orphan File 清理: 回收提交失败或重试后未被有效 Snapshot 引用的游离文件。

图 13:Partition、时间范围与文件统计逐层缩小候选文件;数字仅为查询机制示意,并非维护前后的性能实测。
表维护与查询剪枝解决的是两个相关但不同的问题:前者控制长期碎片和元数据规模,后者利用当前表的分区与统计信息,减少某一次查询真正需要扫描的文件。图中以一个独立的日志查询为例:查询 2024 年 1 月 1 日 10:00--11:00、level='ERROR' 且 status_code >= 500 的记录。候选文件先后经过分区、Manifest 和文件级统计过滤,从 2380 个缩小到 940、420,最终只剩 189 个。这组数字不是 Compact 前后的对比。
这些数字用于解释机制,并不代表某个真实业务的固定性能提升。真正的收益会受到数据分布、文件大小、分区策略、统计信息质量和查询条件影响。重要的是,开放表并非"把所有文件列出来再全量扫描",而是通过多层元数据尽可能让引擎少读文件、少做 I/O。
开放表的价值,在于不绑定单一引擎
数据进入湖仓后,如果只能由写入它的系统读取,就还称不上开放资产。
完整的开放性至少包括三层:
- 格式开放: 数据文件与表元数据遵循 Iceberg 等开放规范;
- Catalog 开放: 引擎可以通过标准化入口发现 Namespace、表和 Snapshot;
- 消费开放: Spark、Flink、Trino、PyIceberg 以及云上分析引擎可以按各自场景复用同一份表数据。

图 14:Catalog 负责发现表和版本,Iceberg 元数据组织文件,多种引擎读取同一份当前表状态。
OSS Tables 提供一个兼容 Apache Iceberg REST Catalog 的 API 端点,该兼容层优先保持与 AWS S3 Tables 的行为一致。对上层引擎而言,Catalog 先返回表的元数据位置,再由 Snapshot、Manifest List、Manifest 逐级定位 DataFile 和 DeleteFile。
这种分层设计带来一个长期收益:写入链路不需要预先决定未来所有计算方式。 今天可以用 Trino 做交互式查询,明天用 Spark 做批处理,后天再把表用于特征工程。计算引擎可以变化,数据资产不必随之搬迁。
当然,"兼容接口"不等于所有扩展能力都完全一致。不同引擎版本、Catalog 接口和 Iceberg 特性的支持矩阵,仍需以当前官方兼容性文档为准。
SQL 补全另一半:让事件在到达时产生结果
TABLE 路径解决了长期沉淀问题,但很多业务不愿等数据先变成表再计算。
例如,对支付成功订单按用户做一分钟窗口聚合,把结果持续写回一个结果 Topic;或者实时过滤异常日志、提取字段、按条件路由到不同下游。这些需求关注的是事件到达后的即时处理,更适合交给 SQL 路径。

图 15:Kafka Topic 映射成流式表,经 SQL 持续处理后写入结果 Topic。
一个概念化的逻辑可以写成:
sql
SELECT
user_id,
TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS window_start,
SUM(amount) AS paid_amount
FROM orders
WHERE status = 'PAID'
GROUP BY
user_id,
这段 SQL 的重点不是具体语法,而是责任边界 :Kafka 提供持续输入和输出,SQL 运行时负责窗口状态、计算资源、失败恢复与观测。各任务独立部署,避免一个任务的资源波动直接影响其他任务。

图 16:产品入口保持统一,每个 SQL 任务使用独立运行时、资源视图和故障域。
TABLE 与 SQL 因而不是替代关系:
- 只需要把原始消息可靠沉淀为开放表,优先考虑消息入湖;
- 需要到达即算的过滤、窗口、聚合和路由,使用流计算;
- 需要复杂关联、自定义算子、跨系统编排或成熟作业治理,仍可选择外部 Flink 等生态计算平台;
- 同一份数据既要实时结果又要长期资产,两条路径可以并行存在。
这也能避免一个常见误区:为了让架构看起来"统一",强行让一种运行时承担所有任务。真正合理的统一,应发生在数据与产品入口,而不是牺牲不同负载应有的隔离。
选择方案之前:先问清四个边界
面对一条新的 Kafka 入湖需求,可以先用四个问题划定边界。
第一,数据只是持续落表,还是还要做复杂计算? 如果只有字段映射、Schema 演进和 CDC 还原,原生能力能减少外部运行时;如果包含复杂 Join、定制 UDF 或多流状态,专业计算引擎更合适。
第二,谁负责长期表健康? 不能只确认"第一批文件是否写成功",还要明确小文件合并、Snapshot 过期和游离文件清理由谁执行、如何观测。
第三,故障后的正确性边界是什么? 需要明确输入进度、文件写入和 Snapshot 提交如何关联;如果下游还有二次写出,端到端 Exactly-once 还依赖目标系统的事务或幂等能力。
第四,未来由哪些引擎消费? Catalog 接口、Iceberg 版本和具体特性需要与 Spark、Flink、Trino 或其他引擎的支持矩阵逐项对齐。
这四个问题能把一次"产品选型",转化为一次可验证的责任划分。
写在最后:让数据同时服务当下与未来
回到本文的主线:存算分离 Kafka 为 Table 与 SQL 提供了共同的数据底座,而这篇文章重点回答的是,如何让 Kafka 消息持续成为一张可信、开放、健康的表。
原生入湖的价值,不是把必要步骤从架构中删除,而是把 Schema、CDC、原子提交、失败恢复和后台维护收进托管边界; 把用户需要长期照看的外部节点,从一条复杂管道收敛为更稳定的 Topic---Table 关系。
与此同时,实时 SQL 为同一份数据提供另一条价值路径:事件到达后立即参与计算,持续产生面向业务的实时结果。
本文对实时 SQL 只做整体定位与边界说明。关于它的运行机制、时间语义、状态管理和一致性实践,我们将在后续文章中继续展开。
最后可以把整套架构浓缩成两句话:TABLE 让消息成为开放资产,SQL 让事件产生实时结果。
两者共享同一个 Kafka 数据起点,各自保持清晰的运行时和责任边界。数据既能在当下创造价值,也能在未来被不同引擎反复使用。
需要说明的是,截至本文整理时(2026 年 8 月),Topic 消息入湖与流计算的开放区域、实例版本、支持语法和公测状态仍在持续演进。消息入湖当前文档标注为公测能力,并对 Serverless 实例版本和地域有要求;流计算的地域、SQL 能力和端到端一致性也存在明确限制。正式开通或发布前,请以控制台与最新官方文档为准。
相关链接:
1《AI 时代,实时入湖正在告别 ETL:从 Kafka 到 Iceberg 的架构减法》
2 消息入湖:将 Kafka Topic 数据持续写入 OSS Table Bucket
help.aliyun.com/zh/oss/user...
3 OSS Tables 快速入门
help.aliyun.com/zh/oss/user...
4 Iceberg REST API 兼容性说明
help.aliyun.com/zh/oss/user...
5 云消息队列 Kafka 版流计算能力说明
help.aliyun.com/zh/apsaramq...
6 流计算架构与高可用
help.aliyun.com/zh/apsaramq...
7 流计算快速入门与使用限制
help.aliyun.com/zh/apsaramq...
线下沙龙:
这条实时链路的下一步,往往是被 AI Agent 消费。Agent 能不能在生产环境里跑得下去,很大程度上取决于上游数据能不能实时可信------上下文靠 T+1 拼出来,时序敏感任务就会频繁失准。
8 月 28 日下午,上海徐汇滨江有一场线下沙龙,就聊一件事:怎么让实时数据以低延迟、高可靠、可弹性的方式,持续供给模型与 Agent。
三位一线工程师沿数据链路依次展开:实时数据流怎么加工成模型可消费的上下文、数据管道要补齐哪些能力才算 AI-Ready、流接入/流计算/入湖能不能收敛进同一个平台。
如果你正在把 AI 应用、Agent 或 RAG 推向生产环境,欢迎来现场。
点击链接,立即报名:survey.aliyun.com/apps/zhilia...
