
如果一张数据库表能被当成消息订阅源来使用下游每次拿到的不是“今天重新全量跑一遍”的数据而是“从上次读完之后发生变化的那几行”你还会不会坚持用定时 ETL 把数据搬到下一层这是 Tabsdata 这个项目最让人印象深刻的地方它把 Pub/Sub 的消息订阅逻辑套在了“表”这个数据开发最熟悉的资源上目标直指传统 ETL Pipeline。我第一次看到“Pub/Sub for Tables to Replace ETL Pipelines”这个定位时觉得这句话很有吸引力但也很容易引起误读。因为它不是在说“消息队列能替代数仓”也不是在说“以后不用做数据清洗了”。它真正想改变的是数据流转方式从“定时跑批、按批搬表”变成“表被订阅后持续推送变更”。这篇文章不是官方文档翻译也不是产品测评。我会从数据工程日常最常遇到的 ETL、ODS 层、增量同步、批量任务这些概念出发拆一下这个方向到底在解决什么问题哪些场景下可以真的减少 ETL 任务哪些场景下你还是得老老实实把调度和治理补上。1. 先看懂题目Pub/Sub for Tables 解决的是表结构数据的实时流转很多人一看到“Pub/Sub”会立刻想到 Kafka、RabbitMQ、云厂商的消息队列服务然后开始纠结“把表变更发到消息队列里不就行了为什么还要再造一个概念”。我觉得这种反应挺正常的。真正要理解 Pub/Sub for Tables得先把它和“CDC 消息队列”的区别分清楚。1.1 它不是给消息队列加了一层 SQL 语法普通消息队列里流动的是一条条消息。消息是事件不是状态。消息消费之后可以删除可以重新消费但消息本身没有“主键冲突”“字段变更”“Schema 演进”这些概念。如果下游拿到的不是一个已经结构化的对象而是 JSON 文本那解析、校验、转换的规则还是得靠下游应用自己维护。Tabsdata 这类方向的思路是把“表”本身当成一个可订阅资源。上游表里新增一行、更新一行、删除一行下游可以通过订阅关系实时感知到变化。再进一步订阅者看到的不再是一堆原始日志而是一张更接近“数据表形态”的变更流。这样做的好处是消费者不需要在每条消息里重新解析字段不用自己拼主键不用手工维护“这条更新属于哪张表”的逻辑。表结构、字段类型、主键这些信息被提升为一等公民。不过这也意味着它不是一个简单的消息转发器而是一个需要理解 Schema、主键、变更语义的数据中间层。1.2 核心是把“表的变化”变成可持续订阅的资源传统做法里如果业务库的订单表每 10 分钟有新数据数仓通常是这样处理的写一个定时任务每 10 分钟查一次订单表把大于上次时间戳的数据拉出来写入 ODS 层再跑后续加工。这个流程里源表只是“一次性查询的对象”。每次都是新任务、新连接、新 SQL每次都要自己维护游标、时间水位或自增 ID。Pub/Sub for Tables 的思路完全反过来源表从“被查询的静态对象”变成“能够持续产生事件的动态主题”。你可以在表上建立订阅消费者自己不会反复去源表做全表扫描而是等待变化事件推送过来。所以它不是取消了 ETL 的存在而是把 ETL 中最容易出错、最难维护的“轮询拉数据”这一段换成了“订阅接收变更”。2. ETL 让人想换掉的点往往出在 ODS 层讨论 ETL 时大家经常提“ETL 的 ODS 层”。如果不做数据仓库可能不清楚这个词但只要你在做数据接入就一定和它打过交道。ODS 全称是 Operational Data Store中文常叫操作数据存储或贴源层。它承载的是“离业务原始数据最近”的那一层。以往绝大多数团队会把业务库的数据先同步到 ODS后续数仓计算再从 ODS 继续往下游加工。2.1 ODS 层为什么最容易堆任务问题不是 ODS 这个层应不应该存在而是很多人把 ODS 层当成“各种临时同步脚本的存放处”。比如订单表要同步到 ODS用户表要同步到 ODS商品表也要同步到 ODS。于是每个表配一个同步任务每个任务都有自己的调度频率、日志目录、重试策略。表多了以后任务数量爆炸互相之间的依赖关系变成一团乱麻。我见过不少团队的 ODS 层表面上是分层清晰的数仓架构实际上每天跑批时都是几十个同步任务排队运行。某个上游接口超时导致下游任务一起失败某张源表加了字段同步任务只覆盖了常用字段新字段始终没有进入数仓。这些问题的共同点不是“清洗逻辑太难”而是“源到 ODS 的数据传递方式太脆弱”。2.2 Pub/Sub for Tables 是在改写 ODS 的交付方式如果把表作为 Pub/Sub 资源ODS 层仍然可以存在但它的交付方式会发生变化。以前是调度系统驱动任务任务连接源库执行 SQL将结果写入 ODS 表。以后可以是源表被发布为一个可供订阅的表资源ODS 层作为其中一个订阅者持续接收变更并落到自己的存储里。业务看数、下游加工继续读 ODS 层。这样带来的第一个好处是不再需要每张表都单独维护一个“增量时间戳”。你不需要反复比较“上次跑到哪了”也不需要担心时间字段在源库里没索引导致慢查询。因为变更事件是基于数据库事务日志或等价机制捕获的而不是业务时间字段猜出来的。第二个好处是处理逻辑和同步逻辑分开了。同步逻辑交给订阅关系负责清洗、去重、聚合这些语义加工继续由 ODS 之后的流程负责。也就是说ODS 层的接入门槛变低了但后续数据治理仍然存在。这里要强调的是ODS 层不是说能删掉而是它可能从“大批量生成的表集合”变成“由订阅关系持续维护的数据集合”。如果你只是为了减少 ODS 层任务数量而导流自己却没有处理好删数、改数、回放这些场景那问题反而会更严重。3. 用这个思路落地之前先梳理数据资产和订阅条件很多人评估这类方案时第一步就在看功能清单支不支持增量、支不支持批量回放、能不能订阅多张表。这些当然重要。但我觉得更优先的是先回到自己的表上去盘一遍哪些表适合被订阅哪些表本身就不适合。3.1 不是每一张表都适合用 Pub/Sub 推送有一类表适合业务明细表、订单表、流水表、日志表、用户行为表。这类表的特点是新增频繁少量更新数据一旦产生就基本固定下游主要按时间维度消费。另一类表要小心配置表、汇总表、价格表、库存表。这类表有时更新很频繁但一条记录会在一天内被反复修改。如果下游只想看每天最终结果而订阅系统把每一次修改都推送出去下游反而会收到大量中间状态。我一般会用这个标准判断下游是希望“见到每次变化”还是只希望“拿到一个尽可能新的结果”。如果希望拿到新结果那么推送原始变更事件给你并不比一张刷新后的快照表更方便。如果下游确实需要感知每一次变化比如事件驱动、审计、实时风控这才适合往 Pub/Sub 方向发展。所以第一步不是问“Tabsdata 能不能订阅这张表”而是问“这张表最值钱的输出形态是什么”。3.2 订阅前把四件事写进验收条件我在做数据同步方案选型时不太先看界面漂不漂亮更关注几个基础能力能不能讲清楚验收维度重点问题说明主键语义每条变更是否能稳定关联到唯一记录没有主键或主键会变化推送时很容易重复变更捕获方式是基于日志捕获还是基于时间戳轮询日志捕获更完整但需要源库开启相应能力保留策略事件多久后会被清理能否重新消费订阅服务不是无限存储要有保留上限回放能力从某个历史点位重新跑到最新是否可行缺少回放能力出问题后只能手工补数据这几个问题不问清楚后面所有实时性优势都会变成数据处理灾难。比如一个订阅消费者挂掉了 30 分钟恢复之后能不能从上次消费到的位置继续处理如果不能这 30 分钟的变更可能直接丢失如果能你需要知道系统保证的是不是“至少一次”如果允许重复下游目标表必须做幂等。另外一个经常被忽略的点是字段级别 Schema 变更。源表加了字段订阅端看到的新事件里多了一个属性旧事件还是旧格式。下游能接受吗如果没有统一处理哪怕 Tabsdata 这类平台自动更新了表结构下游消费逻辑也可能无法平滑适配。4. 是“替代 ETL”还是“替代某个阶段的 ETL”两者完全不一样“Replacement for ETL”这个口号容易让人以为以后可以不用再设计数据任务了。我接触过几个团队后发现真正的问题不是“要不要做 ETL”而是“以前那些 ETL 里很大一部分根本不是 ETL而是无脑搬运”。4.1 适合被替代的机械同步、字段搬运、固定过滤、基础类型转换我把这类数据流叫作“搬砖型 ETL”。它们的典型特征是从 A 库读到 B 库字段基本不变。只做简单过滤和类型转换。目标表结构几乎和源表一一对应。更新频率随着表数量线性增长。任务失败后重跑即可不需要复杂的状态恢复。这类工作用传统 ETL 调度完全能跑但没必要。因为每新增一张源表就要新增一个任务、一套调度配置、一套监控规则。而 Pub/Sub for Tables 把同步抽成“源表发布、目标订阅”新增一张表时只需要定义订阅关系剩余工作由平台处理。这种场景下替换掉的是管道搭建方式不是数据开发岗位。4.2 不适合被替代的复杂 Join、窗口聚合、清洗治理、指标口径、回溯补数下面这些工作我目前不认为仅靠表订阅能直接解决多表关联生成宽表。订阅消息能告诉你订单表和商品表各自发生了什么但关联逻辑本身需要下游计算。复杂窗口聚合。比如计算过去 7 天每个用户的累计订单金额这不是单条消息推送能代替的。质量规则校验。比如空值率、重复率、异常值预警。指标口径维护。同一个指标在报表、算法、运营侧可能定义不同需要专门治理。灾难恢复和回溯补数。如果上游业务库发生一次事故需要从更早的时间点重算这时候稳定的批式回放和快照备份仍然是底线。这里关键区别在于Tabsdata 这类“Pub/Sub for Tables”替代的是“管道”也就是表与表之间的数据搬运而传统 ETL 的“T”和“L”背后还有大量基于业务语义的计算和判断这部分很难被一个消息体系彻底取消。4.3 我建议用一张表看待替代范围ETL 环节是否适合被替换原因源表增量抽取非常适合订阅比轮询的延迟更低、维护成本更低字段名映射部分适合需要确认系统能不能在 Schema 层做映射简单过滤适合可以做成发布端过滤或订阅端过滤增量去重可以前提是系统保证主键和变更顺序一致多表 Join不适合还需要下游计算引擎处理聚合统计不适合实时流和批量跑批各有用处数据质量检查不适合订阅只负责送达不负责证明数据正确历史回放要重点验证不同实现差异很大把这张表想清楚你就不会把“替代 ETL”理解成“消灭数据工程”。它更接近把 ETL 里不产生业务价值的物理搬移任务交给“表订阅”去做让工程师把时间留给真正需要思考逻辑的地方。5. 如果要做概念验证按什么顺序试我对第一次用这类方案的项目最不建议的做法是直接把生产环境最重要的几十张表全部切换过去。更稳妥的流程是先选一张影响面小、更新频率中等、主键清晰的业务表从四个线索做 24 到 72 小时的验证。5.1 第一条线索单表连续变更能否稳定送达不要只看演示环境里插入一条、下游立刻收到了。你要做的事情会更苛刻一点同时验证新增、更新、删除三类操作。连续运行一段足够长的时间不要只测 10 分钟。在下游订阅端做一个小任务统计收到的变更数是否等于源表实际变化的次数。观察如果一条记录被连续更新 5 次下游是收到 5 次事件还是只收到最新状态。这会影响你后面怎么做聚合。这里最容易出现的问题是演示时增量追加很正常一旦源表做批量更新订阅事件数量会突然暴涨。上游一条 SQL 更新了 10 万行如果平台按行生成事件下游要立刻处理 10 万条消息。你不做好积压预估整个订阅关系会变成新的瓶颈。5.2 第二条线索历史数据和最近变更如何衔接很多项目刚接入时目标表里不是完全空的。你需要把存量历史数据先初始化到目标端然后再开启增量订阅。这个初始化过程中如果源表还在持续写入最常见的做法是先做一次快照初始化再从订阅日志的某个准确位点开始拉取。业务要求这里的逻辑必须闭环不能漏掉初始化过程中产生的新变更也不能因为消费者已经从头读取导致重复加载一遍。我在第一次跑这种验证时会在目标表里加一个类似last_updated_at或版本号的字段记录每条数据最后被更新到的时间。这样即使某条数据被重复推送只要重复事件里携带的版本不比当前目标行的版本旧就可以安全忽略。5.3 第三条线索延迟、乱序和重复数据如何处理严格讲分布式系统里“完全有序”是一个非常昂贵的能力。你经常会碰到这种情况记录 A 先更新后删除但删除事件先到。两次更新事件乱序到达目标表最终保留了旧值。下游任务积压时事件到达顺序与源库真实操作顺序不一致。评估时要弄清楚平台提供的是“分区内有序”还是“全局有序”。大多数场景下按主键分区内有序已经够用了并不需要全局有序。你需要做的是在目标端实现幂等写入按主键去重按版本号或业务时间判断哪一条更新生效。5.4 第四条线索失败回放和 Schema 变更谁来负责概念验证里一定要人为制造一次失败。比如让下游消费者停 10 分钟再重新启动看系统能不能恢复处理。几个需要记录的现象恢复后是从上次提交的位置继续还是从头消费如果失败了 100 条消息是自动重试还是进入死信队列谁来检查死信死信消息最终是人工处理还是手工丢弃同时还得验证源表加字段、改字段类型这些情形。如果平台自动同步了 Schema你需要确认下游数据库里的字段类型是否按预期变化如果平台不做 Schema 更新那你要有一个手工变更流程。我的判断标准很简单一个都不能漏比一个都不能重复容易做到但如果既不漏又不重复很难需要在下游端设计幂等保证。6. 最终要改的不是“ETL 任务”而是数据契约把 Tabsdata 这类方向放在更大的数据工程演进里看我发现很多人讨论“替代 ETL”时真正的问题是团队里除了“定时任务”之外没有一个更清晰的方式去描述“表 A 的数据应该以什么频率、什么质量、什么字段维度给表 B”。传统 ETL 中的很多问题不是发生在 SQL 写错了而是发生在数据生产者和数据消费者之间没有明确约定。源表改了字段下游不知道源表删除历史数据下游还在按快照重复统计同一个“订单状态”字段不同消费者理解完全不同。6.1 订阅关系的本质是一份被平台执行的数据契约当表变成可订阅资源时你实际上是在系统层面声明“我发布这张表允许订阅读取这些字段变更粒度是行级变更事件里包含这些元数据。”下游收到的不只是一条数据还有一套可校验的约定。这对团队最大的价值是会逼你把很多以前模糊的东西固定下来。比如主键是什么字段类型是什么一条数据会被更新多少次删除事件是否保留。这些以前散落在各个 SQL 脚本里的经验现在会集中反映在“表发布配置”里。6.2 数据开发的工作重心会从“维护管道”变成“维护边界”以前新增一张业务表第一反应是去调度系统里建一个同步任务然后写清洗逻辑。如果采用表订阅思路第一反应会变成这张表应该发布成什么形态这个观念变化很重要。因为它把问题从“我这一段的 SQL 怎么写”提前到了“这张表的消费者到底需要什么”。消费者需要的是一次变化事件还是一份最新状态还是历史快照对应发布方式应该完全不同。明确边界之后你会发现真正的 ETL 逻辑仍然要写但可以写在更合适的位置而不是写在一堆重复的调度管道里。6.3 真正值得长期保存的能力是回放、幂等、契约校验和语义层最后我想说任何一个工具或平台都很难永远满足所有场景。今天你可能因为一张表换到 Pub/Sub for Tables 的方案减少了任务数量明天如果系统做不到长时间稳定回放、复杂链路可观测、质量规则可配置你仍然会回到自己造轮子或者混合使用批流两条路线。所以与其问“Tabsdata 能不能彻底替代 ETL”不如问你团队的数据管道能不能做到“新表接入不需要改几十行同步代码表结构变更不需要靠人工通知消费逻辑能不能在做本地重构时仍然复用”。踩过几轮之后我的感受是Pub/Sub for Tables 最值得关注的地方不是“实时推送”这个炫技点而是它把数据生产和数据消费之间的约束从文档和口头约定变成了系统可执行的订阅关系。至于跑批调度、复杂加工、指标治理到底要不要保留那要看你的表后面接的是报表、算法还是另一个核心业务系统。把每种表最合适的交付方式想清楚再落地也不迟。