源表删了记录,数仓为什么还在?删除事件怎么传到下游?
本文目录
面试怎么回答
我会先确认删除是状态标记、CDC 事件还是完整快照中的缺失。只拉更新时间无法发现已被物理删除的行。链路要保留业务键、操作类型和可靠的源端顺序,转换与 Sink 都必须支持删除;聚合、缓存和历史表再按各自口径处理。还要保留足够的删除版本信息,防止旧更新重放后把数据复活。全量缺行只有在快照完整、范围一致时才能解释为删除。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
业务库已经删掉一条记录,数仓里的当前状态表却一直留着它。增量任务每天正常结束,新增和修改也都能同步。这里最值得问的不是“任务为什么没跑”,而是这条链路有没有把删除当作一种需要处理的事件。
删除需要独立的变更语义和下游执行策略;缺行不一定是删除,旧事件也不能覆盖更晚的删除状态。
更新时间增量,天然看不到已经消失的行
如果只执行 updated_at 大于某个水位的查询,通常能发现仍在源表中的新增与修改,但被物理删除的行已经不存在,无法再靠扫描当前行把它取出来。
软删除则不同:行仍在,只是 is_deleted 或状态发生改变。是否进入当前状态表、是否保留审计事实,应按业务需求设计,不能看到删除标记就默认删除所有历史。
因此先识别源端删除类型,再选同步机制。任务每天读到新时间,不代表已经覆盖所有变更类型。
CDC 删除事件和墓碑不是一回事
以 Debezium PostgreSQL 连接器为例,删除会有表达操作语义的事件;为配合 Kafka 日志压缩,还可能生成相同 Key、空 Value 的墓碑消息。
删除事件用于传达“哪一行被删除”,墓碑服务于压缩主题的键清理语义。它们不应被理解为消费者一定会收到两次独立业务删除,也不能认为 Kafka 清了键就会替数据库 Sink 执行 DELETE。

链路转换尤其需要注意:如果只提取 after 字段,删除时它可能为空;展开配置若丢弃了删除相关事件,下游就再也不知道该键需要移除。具体选项应按使用中的 Debezium 版本和转换方式核对。
从明细到聚合,删除都要有处理方案
当前状态表可以物理删除或保留逻辑删除状态,但要保证查询口径排除不再有效的记录。审计日志则可能按制度保留操作历史,不能与业务当前视图混成一张含义不清的表。
汇总层不能只删除明细后就结束。SUM、COUNT 可能通过撤回旧贡献修正;去重人数、极值等指标不一定能简单减一,可能需要额外状态或重算受影响范围。
缓存、搜索索引和下游导出也要确认传播路径。删除不是在 DWD 某张表打个标记就天然完成,而是各消费结果如何撤销旧贡献。
涉及个人数据清除时,还需按组织制度和适用要求处理备份与衍生副本。本文讨论工程语义,不对具体系统作出合规判断。
一个典型场景:字段展开时把删除丢掉了
比如当前状态表残留已删账号,最初怀疑 Sink 的删除权限不足。但跟踪消息后发现,源端确实发出了删除,转换层却只保留普通更新后的行,过滤掉了空 after。
根因是简化消息结构时遗漏操作语义。原验收只有新增、字段修改和重试,全部正常,所以连接器健康与任务成功都没能发现问题。
修复应保留业务键、删除类型和必要的源端位置信息,让 Sink 明确识别;测试增加删除、重复删除、删除后重放和下游聚合修正。不要只在 Sink 增加一条从未被触发的删除逻辑。
删除之后,还要防止旧更新把数据复活
假设源端顺序中先更新、后删除,但链路重试使旧更新晚到。如果目标已经物理移除记录,却没有保留足够的删除版本信息,旧更新可能被当作新数据重新插入。
可在受控范围内保留删除状态或版本屏障,拒绝更旧的变更。顺序依据应来自可靠的源端序列或可比较的位置,不能简单比较两台机器的到达时间。
这里的屏障保留多久,要覆盖约定的重放与乱序范围。过早清理会丢掉防复活依据;无限保留又有成本和治理要求,需要明确策略。
合法的后续重建也要能区分。如果业务允许同一个键重新创建,应有更晚的版本或新的实体身份,而不是永远拒绝这个键。
全量快照缺行,什么时候才代表删除?
只有快照完整、范围一致、业务过滤可比时,源快照缺少的键才可能用于判定删除。某个分页没拉完、某个租户没返回,不是批量删除该租户数据的证据。
更稳妥的是先确认批次完成标记、范围清单和必要的对账,再生成候选删除集。大范围异常变化需要门禁,避免一次不完整快照清掉大量正确数据。
最终验收应从源端操作一路追到具体服务结果,明确删除发生、传播、执行和可见的各个阶段。这样才能解释数据为何还在,而不是反复重跑同一个只会新增修改的任务。