想象你经营着一家大型物流仓库,每天有成千上万个包裹入库、改址、退货。客户最关心的不是仓库有多大,而是“我的包裹现在到哪了”。如果每个人都打电话来问,客服早就崩溃了。好在仓库有一本“扫描台账”——每一件包裹被扫码枪扫过,都会按顺序记上一行。真正聪明的做法是安排一个调度员坐在台账旁边:只要台账出现新的一行,就立刻抄送给关心这个包裹的人。这个“盯着台账、照抄变化、实时广播”的机制,就是今天的主角——CDC(Change Data Capture,变更数据捕获)。在分布式系统里,数据库的每一次 INSERT、UPDATE、DELETE 都像包裹的一笔动态,而 Canal 与 Debezium 就是两套成熟的“调度班组”:一个出身阿里巴巴、与 MySQL 深度绑定;一个拥抱 Kafka、多数据库通吃。读完这篇文章,你就能从“台账怎么记”一路看到“通知怎么送”,把整条实时数据链路装进自己的工具箱。

岗位关联

掌握 CDC 的日志解析、同步策略与实时链路设计,是数据平台工程师搭建实时数据体系的核心能力;在 AI 时代,“数据变化传播得有多快”直接决定了模型特征与业务决策能有多快。

目录
  1. 可视化演示
  2. CDC原理概述
  3. binlog日志解析
  4. Canal架构设计
  5. Debezium架构
  6. 数据同步策略
  7. 实时链路实践
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示CDC(变更数据捕获)核心机制:数据流管道模式可视化MySQL binlog经Canal/Debezium解析后通过Kafka投递至下游消费者(ES/缓存/仓库),架构对比模式展示Canal与Debezium的六层架构差异与关键指标对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

CDC数据流管道 MySQL binlog变更源 Canal/Debezium解析捕获 延迟≤100ms 实时同步至下游

CDC原理概述

你经营着一家大型物流仓库,每天有成千上万个包裹入库、改址、退货。客户最关心的不是仓库有多大,而是“我的包裹现在到哪了”。如果每个人都打电话来问,客服早就崩溃了。好在仓库有一本“扫描台账”——每一件包裹被扫码枪扫过,都会按顺序记上一行。真正聪明的做法是安排一个调度员坐在台账旁边:只要台账出现新的一行,就立刻抄送给关心这个包裹的人。这个“盯着台账、照抄变化、实时广播”的机制,就是 CDC(Change Data Capture,变更数据捕获)。

CDC 是一种从数据库日志中捕获数据变更、并把变更推送给下游系统的技术。它不修改业务代码,不要求业务方在写库的同时“顺便”通知别人,而是让数据库自己留下的日志成为唯一事实来源。对 AI 从业者而言,这相当于给数据平台接上了一根“神经”:实时特征、模型样本、缓存更新、搜索索引,全都依赖这根神经传递的变化信号。

在日志解析方案流行之前,业界常见两种笨办法。一是全表轮询:每隔几秒 SELECT 一次全表,和上次对比找出变化,白白消耗数据库 IO,延迟还高。二是应用双写:业务代码里同时写数据库和消息队列,侵入性强、容易漏写。相比之下,日志解析方案直接读取 MySQL binlog——数据库本身就把每次变更写进了日志,我们只是去“照抄”,因而低侵入、低延迟、高可靠,还能拿到变更前后的数据镜像。

就像调度员抄写台账的速度决定了客户收到通知的快慢,CDC 方案的好坏,第一个要看的就是“延迟”——从数据库提交事务,到下游真正拿到消息,中间隔了多久。延迟越低,业务越接近“实时”;延迟一旦抖动,往往意味着解析、网络或消费端出现了瓶颈。我们把这份延迟写成一个公式,它贯穿整条链路的设计。

ΔT=Tsink−Tcommit\displaystyle \Delta T = T_{\text{sink}} - T_{\text{commit}}
这个公式衡量“数据库写完到下游收到”的耗时,是评价 CDC 链路最直接的指标。
  1. 记录提交时刻数据库事务执行成功的那一刻,记下时间戳 T_commit,它是整条链路计时的起点,相当于包裹扫码入库的时刻。
    TcommitT_{\text{commit}}
  2. 记录到达时刻下游系统真正收到并处理完这条事件时,记下时间戳 T_sink,相当于客户收到通知的时刻。
    TsinkT_{\text{sink}}
  3. 相减得到延迟用到达时刻减去提交时刻,得到延迟 ΔT。延迟越小,说明调度员抄送得越及时。
    ΔT=Tsink−Tcommit\Delta T = T_{\text{sink}} - T_{\text{commit}}

binlog日志解析

为了让每一笔包裹动态都有据可查,仓库规定:所有扫码记录必须按时间顺序写进台账,不许撕页、不许插队。MySQL 也有一本这样的“台账”——binlog(二进制日志),它按顺序记录所有改变数据的操作,包括插入、更新、删除,甚至建表。它不是给人看的,而是给机器“追剧”用的:日积月累,就是数据库的一生。

你可能早就听说过 MySQL 主从复制:主库写 binlog,从库把 binlog 拉到本地“重放”,于是从库和主库拥有相同的数据副本。有趣的是,Canal、Debezium 正是搭了这趟顺风车:它们把自己伪装成 MySQL 的一个从库,向主库申请 binlog 的读取权限,但拿到日志后并不重放 SQL,而是把日志解析成一条条结构化的“变更事件”,转卖给下游。

binlog 有三种记录格式:statement 记录 SQL 语句本身,日志小但结果不可控;row 模式记录每一行数据变更的“前镜像”和“后镜像”,精确无损,是 CDC 工具最爱的格式;mixed 则是两者混合。在主从复制里,“从库读到哪一行”由位点(position)标记;在 CDC 里,同样靠位点实现断点续传——下次启动,从上次读到的位置继续。

台账不是无限期的,binlog 也默认只保留若干天(例如 expire_logs_days)。这意味着 CDC 擅长的是“从今天开始往后的每一笔”,而不是“把十年前的历史也找回来”。历史数据要用全量快照或备份来补齐——这个分工,后面的同步策略会细说。

posnext=poscurrent+len(event)\displaystyle pos_{\text{next}} = pos_{\text{current}} + \text{len}(\text{event})
位点推进公式:解析器靠“当前位置 + 事件长度”不断向前移动,这就是断点续传能恢复的基础。
  1. 读取当前位点pos_current 表示解析器已读到日志文件的哪个字节偏移,相当于台账翻到第几行。
    poscurrentpos_{\text{current}}
  2. 计算事件长度len(event) 表示刚读到的这条变更事件占了多少字节,相当于台账上一行记录占几格。
    len(event)\text{len}(\text{event})
  3. 推进位点两者相加得到下一条事件的起始位置,这就是需要持久化的断点续传值。
    posnext=poscurrent+len(event)pos_{\text{next}} = pos_{\text{current}} + \text{len}(\text{event})

Canal架构设计

现在给仓库正式配备一个调度组,名字叫 Canal。这个组内部有明确分工:有人守在台账前逐行抄录(Parser),有人把抄录内容翻译成统一格式的通知单(Sink),有人把通知单暂时存放在中转盒中(Store),最后还有专门的联络员把通知单送给外面的客户(Client)。四个人各司其职,整条抄送流水线就不会堵。

Canal 是阿里巴巴开源的中间件,核心机制就是伪装成 MySQL 从库,向主库发送 dump 协议请求,持续读取 binlog。它的基本运行单元叫 instance,内部由三大模块组成:EventParser 负责连接主库、拉取并解析 binlog;EventSink 负责过滤、加工和分发事件;EventStore 负责在内存中暂存解析好的事件,等待客户端来取。

光有流水线还不够,还要保证“消息不丢”。Canal 采用 ack/rollback 机制:客户端消费完一条消息后回传 ack,Canal 才推进位点;如果消费失败,可以 rollback 让消息留在 Store 里重新投递。另外,Canal 支持集群高可用:通过 ZooKeeper 协调多台 server,同一个 instance 同一时刻只有一台在解析日志,故障时自动切换,客户几乎无感知。

一句话总结,Canal 是一支为 MySQL 量身定制的“抄送班组”,性能强劲、与 Java 生态配合默契,但和 MySQL 绑定较紧。如果你的数据源不止 MySQL,或者想要一套更“标准化”的通知协议,那就该认识另一支队伍——Debezium。

Sfiltered={e∈Eall∣table(e)∈Tallow}\displaystyle S_{\text{filtered}} = \{ e \in E_{\text{all}} \mid \text{table}(e) \in T_{\text{allow}} \}
事件过滤公式:不是台账上每一行都要抄送,Canal 的 EventSink 只保留白名单表的事件。
  1. 定义全集E_all 是 binlog 中所有变更事件的集合,相当于台账上全部扫描记录。
    EallE_{\text{all}}
  2. 设定过滤条件对每条事件 e 取出所属表名 table(e),判断它是否在允许列表 T_allow 中。
    table(e)∈Tallow\text{table}(e) \in T_{\text{allow}}
  3. 取出子集满足条件的事件才被保留转发,不满足的丢弃。
    Sfiltered={e∈Eall∣table(e)∈Tallow}S_{\text{filtered}} = \{ e \in E_{\text{all}} \mid \text{table}(e) \in T_{\text{allow}} \}

Debezium架构

假设你的公司同时租用了好几家仓库,还对接了无数外部系统。这时你会希望所有通知沿用统一格式,不管包裹来自哪个仓库,客户收到的通知单长一个样。Debezium 就是这支讲究“标准协议”的调度队。它把自己建立在 Kafka Connect 之上,把所有数据库的变化都编码成一种通用记录格式,谁拿到都能直接消费。

Debezium 是一个开源分布式平台,通过“连接器(Connector)”支持 MySQL、PostgreSQL、MongoDB、Oracle 等多种数据库。每个连接器负责盯一个数据源,以 Kafka Connect Source Connector 的身份运行,把捕获到的变更持续写入 Kafka 的 topic。对使用者来说,部署变成了“启动一个连接器”,而不是维护一套自研工具。

Debezium 输出的记录称为 Source Record,核心字段包括:source(数据库、表名、binlog 位点等来源信息)、op(操作类型,c 表示新增、u 表示更新、d 表示删除、r 表示快照记录)、ts_ms(事件发生的时间戳)以及 before/after(变更前后的数据)。它还内置快照机制:首次启动会自动做一次全量快照,随后无缝切换成增量监听,整个过程对业务透明。

对比 Canal 和 Debezium:前者轻量、贴近 MySQL、性能调优空间大;后者更标准化、多数据库支持好、与 Kafka 生态天然融合。选型没有绝对优劣,关键看你的数据源是“纯 MySQL 电商后端”还是“多源异构的数据湖或微服务”。无论选谁,都绕不开一个问题:历史数据怎么补?增量从哪开始?这就是下一节的同步策略。

Stotal=Ssnapshot∪Sincremental\displaystyle S_{\text{total}} = S_{\text{snapshot}} \cup S_{\text{incremental}}
完整数据 = 全量快照 ∪ 增量变化:Debezium 先拍快照再追增量,两者取并集才能得到完整视图。
  1. 全量快照S_snapshot 是启动时捕获的一份完整数据,相当于大盘点登记造册的结果。
    SsnapshotS_{\text{snapshot}}
  2. 增量变更S_incremental 是快照之后数据库继续产生的所有变更事件。
    SincrementalS_{\text{incremental}}
  3. 取并集两者相并才是完整数据视图,缺了任何一部分,目标库都不完整。
    Stotal=Ssnapshot∪SincrementalS_{\text{total}} = S_{\text{snapshot}} \cup S_{\text{incremental}}

数据同步策略

接一个新客户系统,老板要求:把所有包裹当前状态同步过去,并且此后一直保持最新。聪明的做法分两步:先安排一次彻底盘点,把此刻仓库里所有包裹登记造册;然后让调度员持续跟踪新的进出记录。麻烦的是,盘点过程里包裹还在不断进出——怎么保证盘点结束时不重不漏?这时需要给两条流水线约定一个“交接点”。

第一步全量同步,通常用快照工具把当前数据整体搬走,同时记录快照那一刻 binlog 的位点;第二步增量同步,从那个位点开始持续消费 binlog 中新发生的变更并写入目标。这个“交接点”就是位点(position / offset),如同台账上的书签,精确记录“我已经读到哪一行”。全量与增量以位点为界,无缝衔接。

位点管理直接决定同步的可靠程度。Canal 以客户端 ack 为前提推进位点;Debezium 则把 offset 定时提交到 Kafka 内部 topic,重启后从已提交位点继续。由于网络和重启都可能带来重复投递,业界普遍采用“至少一次投递 + 下游幂等”的组合:下游按主键做 upsert 或去重,同一事件处理两遍结果一样,从而做到不丢不重。

大白话就是:盘点负责“把存量搬过去”,调度员负责“把增量追上来”,位点是两者之间唯一的接力棒。接力棒保存得好,系统重启一百次也不怕;保存得含糊,轻则重复消费,重则静默丢数据。下一节,我们把这些零件装进一条完整的实时链路,看看它怎么在真实世界转起来。

possaved=max⁡(pospersist, posack)\displaystyle pos_{\text{saved}} = \max(pos_{\text{persist}},\ pos_{\text{ack}})
位点保存公式:只有下游确认成功的位点才能作为保存值,保证断点续传时不丢消息。
  1. 已落盘位点pos_persist 是同步服务定期写入存储的位点,相当于把书签夹在台账某处。
    pospersistpos_{\text{persist}}
  2. 已确认位点pos_ack 是下游回执 ack 之后才更新的位点,代表“这行之前的消息都处理完了”。
    posackpos_{\text{ack}}
  3. 取较晚者位点只能前进不能倒退,所以取两者的最大值作为保存值,防止重启后丢消息。
    possaved=max⁡(pospersist, posack)pos_{\text{saved}} = \max(pos_{\text{persist}},\ pos_{\text{ack}})

实时链路实践

至此,整个通知中心已经成型:扫码枪产生记录 → 调度员抄写翻译 → 放进中转站 → 客户取走通知单。对应到技术栈,就是 MySQL 写 binlog → Canal / Debezium 解析 → 消息队列(Kafka / RocketMQ)缓冲分发 → 下游应用消费。每个环节职责单一,任何一环故障都不会拖垮整条链路。

消息队列是这条链路的中转站。Canal 可以通过内置 adapter 把事件写入 Kafka 或 RocketMQ,Debezium 则天生以 Kafka Connect Source 的身份写入 topic。下游按主题订阅,像订杂志一样各取所需:订单组只订订单 topic,用户组只订用户 topic。消费端组成消费组水平扩展,处理完一条提交一次 offset,提交成功才代表这条消息“消化”了。

故障恢复是链路设计的试金石。下游服务挂了不要紧,消息留在队列里,offset 停在那里,等恢复后继续消费即可;同步进程整个宕机,重启后从持久化位点继续读日志,最多重复、不会丢失。真正需要盯紧的是监控指标:lag(消费积压量)、解析吞吐、错误率、位点是否停滞。lag 一旦持续上涨,说明消费能力跟不上生产速度,就得扩容或优化。

回头看这条链路:数据库每一次细微变动,都被日志忠实记录,被调度工具实时捕获,被消息队列稳稳传递,最终被下游系统及时消化——从“客户打电话问包裹”到“包裹一动全公司都知道”,这就是 CDC 给分布式系统带来的秩序感。而这份秩序,正是数据平台工程师在 AI 时代最值钱的基本功。

L=posproducer−posconsumer\displaystyle L = pos_{\text{producer}} - pos_{\text{consumer}}
lag 计算公式:队列最新位置减去消费组已处理位置,差值就是积压消息数,是链路健康度的晴雨表。
  1. 生产端位置pos_producer 是消息队列中最新一条消息的位置,相当于中转站货架上最新的通知单编号。
    posproducerpos_{\text{producer}}
  2. 消费端位置pos_consumer 是当前消费组已成功处理到的位置,相当于客户已经取走的通知单编号。
    posconsumerpos_{\text{consumer}}
  3. 求差得到积压量差值 L 就是 lag,反映还有多少消息在排队等待处理。
    L=posproducer−posconsumerL = pos_{\text{producer}} - pos_{\text{consumer}}

AI 应用场景

实时特征与推荐系统

阶段:特征工程 / 实时数据接入

解决的问题:用户点击、下单等行为写入 MySQL 后,传统批处理要等数小时才能刷新特征,推荐效果严重滞后。CDC 把订单、行为表的变更实时推送出来,特征平台秒级刷新,模型才能“跟上用户的手速”。

具体案例:某电商在大促期间用 Canal 监听订单表 binlog,把订单状态变化写入 Kafka,实时计算用户累计消费金额、类目偏好等特征,驱动推荐排序和风控决策,整体特征延迟从小时级降到秒级。

缓存与搜索引擎一致性

阶段:数据同步 / 索引更新

解决的问题:业务代码双写数据库和 Elasticsearch,容易漏写或不一致。改用 CDC 单向监听,数据库为主、下游索引跟着日志走,彻底摆脱双写负担。

具体案例:商品中心修改价格后,Debezium 捕获 UPDATE 事件并写入 Kafka,消费端据此更新 ES 商品索引和 Redis 缓存,搜索结果与数据库价格保持一致,误写概率大幅下降。

异构数据库与数据湖入湖

阶段:数据采集 / 数据湖构建

解决的问题:多套业务库要汇入数据湖供 AI 训练,定时抽取延迟高、增量难追踪。用 Debezium 把 MySQL、PostgreSQL 的变更实时写入 Kafka,再落成数据湖表,样本 T+0 可用。

具体案例:某数据平台团队为核心交易库部署 Debezium 连接器,把 binlog 变更以近似实时的速度写入数据湖的 Delta 表,模型训练样本从“前一天快照”升级为“当天实时流水”。

Python 演示

python86 行
"""Simulate a CDC pipeline: read binlog events, parse, push to MQ."""import json  class BinlogReader:    """Simulate MySQL binlog; yield change events one by one."""     def __init__(self, events):        self.events = events        self._pos = 0     def read_event(self):        if self._pos >= len(self.events):            return None        event = self.events[self._pos]        self._pos += 1        return event  class CanalParser:    """Simulate Canal; parse raw events and filter by table."""     def __init__(self, tables=None):        self.tables = list(tables or [])        self.gc_count = 0     def parse(self, event):        if self.tables and event["table"] not in self.tables:            return None        self.gc_count += 1        return {            "schema": event["db"],            "table": event["table"],            "eventType": event["type"],            "data": event["data"],            "old": event.get("old"),        }  class MessageQueue:    """Simulate a MQ buffer between producer and consumer."""     def __init__(self):        self.items = []     def publish(self, msg):        self.items.append(msg)     def consume(self):        if not self.items:            return None        return self.items.pop(0)  def main():    events = [        {"db": "shop", "table": "orders", "type": "INSERT",         "data": {"id": 1, "amount": 99, "status": "paid"}},        {"db": "shop", "table": "orders", "type": "UPDATE",         "data": {"id": 1, "status": "shipped"}, "old": {"status": "paid"}},        {"db": "shop", "table": "orders", "type": "DELETE",         "data": {"id": 2, "amount": 50, "status": "cancel"}},    ]    reader = BinlogReader(events)    parser = CanalParser(tables=["orders"])    mq = MessageQueue()     while True:        raw = reader.read_event()        if raw is None:            break        msg = parser.parse(raw)        if msg is not None:            mq.publish(json.dumps(msg))            print("pushed:", msg["eventType"], msg["table"], msg["data"])     print("queue size:", len(mq.items))    while True:        msg = mq.consume()        if msg is None:            break        print("consumed:", msg)  if __name__ == "__main__":    main()
目的:模拟一条微型 CDC 链路——BinlogReader 扮演 MySQL 日志源,CanalParser 扮演 Canal 的解析与过滤,MessageQueue 扮演消息队列中转,完整演示“变更捕获 → 解析 → 推送 → 消费”的全过程。
  1. class BinlogReader:模拟 MySQL 的 binlog 数据源,就像仓库的扫描台账。
  2. self._pos = 0记录日志读到哪一行,等价于 binlog 的位点(position)。
  3. def read_event(self):每次调用返回一条变更记录,模拟 Canal 按顺序读取日志。
  4. class CanalParser:模拟 Canal 的解析模块,负责把原始日志变成结构化消息。
  5. self.tables = list(tables or [])保存允许转发的表白名单,对应 Canal 的过滤配置。
  6. def parse(self, event):按表名过滤并转换事件,等价于 Canal 的 filter + parser。
  7. if self.tables and event["table"] not in self.tables:只转发关注表的变更,避免下游收到无关数据。
  8. mq.publish(json.dumps(msg))把结构化消息序列化,等价于投递给 MQ 的字节流。
  9. def consume(self):模拟下游消费者从队列拉取变更。
  10. if __name__ == "__main__":可执行入口,直接运行即可看到推送与消费的完整日志。

常见误区

误区我听说主从复制也是读 binlog,Canal 也是读 binlog,那它们不就是一回事吗?部署了 Canal 是不是就不用配从库了?

正解主从复制是把 binlog 拿到另一台 MySQL 上“重放”,目的是得到一份一模一样的数据副本;CDC 是把 binlog 解析成“变更事件”推送给任意下游系统(缓存、搜索引擎、消息队列),目的是广播变化。可以说它们同源(都靠 binlog)但不同路:一个复制数据,一个分发事件。比如主从复制不会告诉你有条订单从“待支付”变成了“已支付”,而 CDC 会把这条 UPDATE 连同前后镜像都告诉你。

为何常见:两者都伪装成从库或读取 binlog,机制相似,初学者容易把“复制数据”和“分发事件”混为一谈。

误区既然是“实时”同步,那我在数据库里 UPDATE 一条,下游应该立刻(0 毫秒)就看到吧?

正解CDC 的“实时”是指近实时,一般毫秒到秒级。链条上每一环都有开销:binlog 写入与 dump、解析器处理、网络传输、消息队列排队、消费端落库。用公式衡量就是 ΔT = T_sink − T_commit。如果 lag 涨到几十秒,就该检查消费端是不是出现了瓶颈,而不是怀疑 CDC 本身。

为何常见:“实时”这个词容易让人误以为是零延迟,忽略了分布式系统中每一跳传输和处理的物理成本。

误区既然 binlog 记录了所有变更,那我想看三个月前的数据,让 Canal 从开头再读一遍不就行了?

正解binlog 是滚动保留的,默认只有几天(expire_logs_days / binlog_expire_logs_seconds),最早的历史早已被清理。CDC 的定位是增量同步,历史存量要靠全量快照(mysqldump、Debezium snapshot)或备份来补齐。正确姿势是“先全量、再增量”,靠位点衔接,而不是指望日志无限回放。

为何常见:把 binlog 当成永久档案,忽略了数据库的滚动清理机制。

误区Canal/Debezium 已经把消息推送出去了,而且我消费端都收到了,数据一定不会丢,不用再做什么了。

正解消息“送达”不等于“处理成功”。Canal 的 ack 只代表客户端从 Store 取走了消息;Debezium 提交 offset 只代表连接器把数据写进了 Kafka。下游把数据写进自己的库、更新缓存,这一段的可靠性要靠消费端自己兜底:重试、幂等、失败重投。所以生产实践中“至少一次 + 幂等”才是标配。

为何常见:把“投递成功”和“消费成功”两个层面的语义混在了一起。

面试题

Q1. CDC 的核心思想是什么?

单选题 · easy

  • A.定期轮询数据库全表数据
  • B.解析数据库日志获取变更← 正确答案
  • C.让数据库主动推送所有数据
  • D.给业务数据库加触发器

答案:B。CDC 的本质是“读日志、抄变化”,对应台账比喻就是调度员抄台账。轮询是主动翻包裹,效率低且延迟高;双写和触发器侵入业务代码,都不是 CDC 的典型思路。

Q2. Canal 解析 MySQL 的哪种日志来捕获变更?

单选题 · easy

  • A.错误日志 error log
  • B.慢查询日志 slow log
  • C.二进制日志 binlog← 正确答案
  • D.通用日志 general log

答案:C。只有 binlog 按顺序记录了所有数据变更,主从复制和 CDC 都以它为数据源;错误日志、慢查询日志都与数据变更无关,就像台账只有扫描记录,其他本子记的不是包裹动态。

Q3. Canal 在协议层面把自己伪装成什么?

单选题 · mid

  • A.伪装成 MySQL 客户端
  • B.伪装成 MySQL 从库 slave← 正确答案
  • C.伪装成 MySQL 主库 master
  • D.伪装成独立数据库代理

答案:B。Canal 利用主从复制协议,向主库发送 dump 请求,主库把它当成正常从库持续推送 binlog。伪装成客户端拿不到 binlog 流,伪装成主库方向反了,代理则不是协议层面的身份。

Q4. Debezium 最常与哪种消息系统深度集成?

单选题 · mid

  • A.Kafka 消息系统← 正确答案
  • B.RocketMQ 消息系统
  • C.不依赖任何消息系统
  • D.仅支持文件输出

答案:A。Debezium 构建在 Kafka Connect 之上,连接器天然把变更写入 Kafka topic,Kafka 是它的主战场;RocketMQ 更多与 Canal 配合使用。

Q5. binlog 的 row 格式下,一条 UPDATE 事件通常包含什么?

单选题 · mid

  • A.仅保存变更前的旧值镜像
  • B.仅保存变更后的新值镜像
  • C.同时保存前后两份数据镜像← 正确答案
  • D.保存完整执行的 SQL 文本

答案:C。row 格式记录每行变更的 before 和 after 两份镜像,下游可以精确知道“从什么变成什么”,这正是 CDC 偏好的格式;statement 格式才保存 SQL 文本。

Q6. 关于位点(position/offset)管理,下列哪项说法正确?

单选题 · hard

  • A.位点一旦保存就不可再更新
  • B.位点记录消费进度并支持断点恢复← 正确答案
  • C.位点只存在内存中不会落盘
  • D.位点与消息队列的消费无关

答案:B。位点相当于台账书签,标记“已读到哪一行”。Canal 在 ack 后推进位点,Debezium 把 offset 提交到 Kafka topic,都是为了重启后从断点继续。A、C、D 都错在把位点描述成静止、内存态或与消费无关。

Q7. 全量+增量同步中,增量同步的起点如何确定?

单选题 · hard

  • A.从 binlog 开头全部重新解析
  • B.以全量快照时刻的位点为增量起点← 正确答案
  • C.随机选择一个时间点开始
  • D.只能从当前时刻开始同步

答案:B。先做全量快照并记录当时的 binlog 位点,快照完成后再从该位点消费增量,二者用“交接点”衔接,才能不重不漏。从开头解析不现实(binlog 会被清理),随机选点会造成数据缺口。

Q8. 关于 Canal 与 Debezium 的说法,下列哪些是正确的?

多选题 · mid

  • A.两者都以解析数据库日志为手段← 正确答案
  • B.Canal 只能把数据写入本地文件
  • C.Debezium 常通过 Kafka Connect 部署← 正确答案
  • D.两者都能取代数据库主从复制

答案:AC。两者都通过读 binlog 或日志捕获变更,这是 CDC 的核心;Debezium 以 Kafka Connect 连接器形式运行。Canal 可对接 MQ 等多种输出,不是只能写文件;CDC 是“广播事件”,替代不了“复制数据副本”的主从复制。

Q9. 为保证 CDC 链路不丢数据,常用手段包括哪些?

多选题 · hard

  • A.消费成功后回传 ack 并保存位点← 正确答案
  • B.下游消费逻辑做幂等处理← 正确答案
  • C.关闭 binlog 以降低开销
  • D.消息队列持久化并启用副本← 正确答案

答案:ABD。ack 加位点保证“断点续传”,幂等消化“重复投递”,MQ 持久化和副本保证“消息不因宕机丢失”。关闭 binlog 等于拔掉数据源,数据源都没有了,何谈捕获?

Q10. CDC 最适合解决下面哪类问题?

单选题 · easy

  • A.把数据库变更实时同步到下游系统← 正确答案
  • B.代替业务应用日志的记录
  • C.提升复杂 SQL 查询的性能
  • D.压缩数据库磁盘的存储空间

答案:A。CDC 的价值就是把数据库增删改实时广播给缓存、搜索、数仓、特征平台等下游,相当于调度员把台账变化抄送给所有关心的人。它不负责日志记录、SQL 优化或存储压缩——那些是其他工具的活。