亚马逊AWS官方博客

RDS for MySQL 8.0 升级 8.4 之下游 Binlog 消费处理实战 (一)—— 蓝绿部署下的 Canal CDC 位点衔接与故障处理

摘要:本文基于一次 RDS for MySQL 8.0 升级至 8.4 的蓝绿切换演练,分析即使正确开启了 GTID 模式、Canal 切换到绿环境只读副本后仍然出现 errno 1236 的原因,说明 GTID 位点的修补方法、Kafka 追赶阶段的消息大小限制,以及适用于该演练拓扑的切换前检查、故障处置与切换后验证步骤。 本文的结论基于 Canal 以 GTID 模式订阅 RDS for MySQL 只读副本、通过蓝绿部署升级至 MySQL 8.4。


一、引言

MySQL 8.0 的标准支持已于 2026 年 7 月 31 日结束。此后仍运行 RDS for MySQL 8.0 的实例会自动转入 Amazon RDS Extended Support 并按其定价计费,升级到 8.4 LTS 可以结束这部分费用。

很多 MySQL 用户会用 Canal 这类工具实时订阅 binlog,把数据库变更投递到 Kafka 等消息系统,供缓存刷新、索引构建、数仓同步等下游任务消费。RDS 蓝绿部署(Blue/Green Deployments)是 AWS 推荐的低停机升级方式,但对 Canal 这种”伪装成 MySQL 从库”的外部 binlog 消费者来说,蓝绿切换意味着数据源被整体换成一套全新的实例。此前的博客《Aurora MySQL 2 升级之下游 Binlog 消费处理方案》已经验证过:GTID 模式是蓝绿切换下 Canal 不丢不重续传的前提。本文基于一次 RDS for MySQL 8.0 升级至 8.4 的蓝绿切换演练,分析即使正确开启了 GTID 模式、Canal 切换到绿环境只读副本后仍然出现 errno 1236 的原因,说明 GTID 位点的修补方法、Kafka 追赶阶段的消息大小限制,以及适用于该演练拓扑的切换前检查、故障处置与切换后验证步骤。

本文的结论基于 Canal 以 GTID 模式订阅 RDS for MySQL 只读副本、通过蓝绿部署升级至 MySQL 8.4、并将变更写入 Kafka 的演练环境。

二、GTID 模式下的 Canal 消费机制

在 GTID 模式下(canal.instance.gtidon = true),Canal 以 GTID 集合而不是 binlog 文件名加偏移量来记录消费位点。跨实例续传(蓝绿切换正是这种场景)靠的就是这个机制。

GTID Auto-Positioning 是 MySQL 复制层的定位机制,从库通过 CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION = 1 启用。COM_BINLOG_DUMP_GTID 是客户端请求 GTID binlog dump 时使用的协议命令,与基于文件名和偏移量的 COM_BINLOG_DUMP 相对应。

Canal 不执行从库配置语句。启用 canal.instance.gtidon = true 后,Canal 在连接源库时构造 COM_BINLOG_DUMP_GTID 请求,并携带自己已处理的 GTID 集合;在服务端看来,它与一台开启了 Auto-Positioning 的从库没有区别。

服务端的处理过程如下:

  1. 从库上报的是集合,不是位点。连接时从库不说”从某文件某偏移量开始”,而是把自己完整的已执行 GTID 集合发过去(形如 uuid1:1-876334, uuid2:1-8);
  2. 服务端做集合减法。发送内容 = 服务端的 gtid_executed − 从库上报的集合。服务端从最早一个包含所需事务的 binlog 文件开始扫(依据每个文件头部的 Previous_gtids 事件),把差集里的事务依次发出,从库已有的自动跳过;
  3. 校验是”全有或全无”的。差集中只要有一个事务不在服务端的可用 binlog 里(也就是落在 gtid_purged 里),服务端不会跳过它继续发,而是直接用 errno 1236 拒绝整个连接——这正是本文第三章故障的协议根源;
  4. 对”多余”的 GTID 反而宽容。副本集合里有服务端不认识的外来 UUID(比如旧环境只读副本的本地事务)时,服务端忽略它,不报错。

Canal 能否在切换后继续读取,不取决于旧实例的 binlog 文件名和偏移量,而取决于新接入端能否从可用 binlog 中提供 GTID 差集里的全部事务。

版本兼容性方面有一个硬性门槛:MySQL 8.4 移除了 SHOW MASTER STATUS 等语句,替代为 SHOW BINARY LOG STATUS。Canal 对 8.4 的语法适配由 PR #5231 引入(2024-08-29 合入 master):MysqlConnection 增加 atLeastMySQL84() 判断,MysqlEventParser 据此在这两个语句之间切换。该提交最早包含于 canal-1.1.8-alpha-3,正式版从 canal-1.1.8 起具备;canal-1.1.7 及以下、以及 1.1.8 的 alpha-1、alpha-2 不含此适配,对 8.4 寻位会报 syntax error。另外 8.4 默认禁用 mysql_native_password,Canal 复制账号需要提前迁移到 caching_sha2_password

三、环境与架构

演练环境如下:

组件 配置
源数据库 RDS for MySQL 8.0.4x,主库(Multi-AZ)→ 只读副本(Multi-AZ)
升级目标 RDS for MySQL 8.4(通过蓝绿部署创建绿环境)
GTID gtid_mode = ONenforce_gtid_consistency = ON
Canal Canal Server 1.1.8(gtidon = true),订阅只读副本的 binlog
下游 Amazon MSK(Kafka),Canal 经 Kafka producer 投递变更
造数端 持续写入脚本模拟业务流量,演练全程不停

数据链路架构如下图所示:

图 1:蓝绿切换前后 Canal 的数据链路

[图 1:蓝绿切换前后 Canal 的数据链路]

RO 域名保持不变,切换时底层实例由蓝只读副本换成绿只读副本;图中同时标注了各实例的 server_uuid 与 GTID 区间,是第三章问题分析的基础。

切换完成后,原生产环境的 endpoint 名称会被分配给新的生产实例:RO 域名不变,域名背后的实例从蓝只读副本换成绿只读副本,底层 IP 可能发生变化。因此 Canal 应配置 RDS DNS endpoint,而不是解析结果中的某个 IP 地址。如果 Canal 固定连接切换前解析到的 IP,连接可能继续指向旧环境,或在旧实例不可用后直接失败。检查 Canal 进程的 DNS 缓存策略。确认 JVM 不会无限期缓存 RDS endpoint 的 DNS 解析结果,并在预生产环境验证 switchover 后 Canal 能重新解析到新生产实例。

四、切换演练:GTID 模式下依然出现 errno 1236

4.1 现象

蓝绿切换完成后,Canal 重连 RO 域名,日志持续报错(已脱敏):

[destination = test-instance , address = mysql8-test-ro.********.us-west-2.rds.amazonaws.com/10.*.*.14:3306 , EventParser]
ERROR c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy -
java.io.IOException: Received error packet: errno = 1236, sqlstate = HY000
errmsg = Cannot replicate because the source purged required binary logs. ...
The GTID set sent by the replica is
'1540d7c2-...:1-76910, 72284206-...:1-8, a9fa6801-...:1-876334',
and the missing transactions are '5ca50a2f-...:1-16'

十几分钟后再看,Canal 中间自动回退过位点、也重试过,但报错一字不差:服务端坚持认为 Canal 缺 5ca50a2f-...:1-16 这 16 个事务,而且它们所在的 binlog 已经被清掉,补发不了。

造数端的业务事务(挂在蓝主库 UUID a9fa6801 下)Canal 已经全部消费完了,1-876334 正是切换点的定格值。也就是说,Canal 没缺任何业务数据,缺的是一个它从没见过的 UUID 的头 16 个事务。

4.2 原理分析:新 UUID 从哪来,为什么补不回来

GTID 的形式是 server_uuid:事务序号。启用 binlog 的实例对本地执行的事务记录自身的 server_uuid;通过复制传递的事务保留源实例的 UUID。

本次演练中,绿环境的两个实例都使用了与蓝环境不同的 server_uuid。把演练中观察到的所有 UUID 与实例对应起来:

UUID(缩写) 归属实例 GTID 区间表现
1540d7c2 蓝侧历史实例 1-76910,全程静止
a9fa6801 蓝环境主库(8.0) 切换前持续增长,切换点定格在 1-876334
72284206 蓝环境只读副本 1-8,副本本地的托管操作
5ca50a2f 绿环境主库(8.4) 切换前 1-16(创建/升级期内部事务);切换后业务写入持续增长
c00d4e46 绿环境只读副本 1-8(副本本地的托管操作)

那 16 个事务,是 RDS 托管服务在创建绿主库、配置复制、执行引擎升级过程中的内部写操作,数量因流程而异,不是个固定值。导致 errno 1236 的关键在于绿环境主库创建时的事务可见性:

  1. RDS 先创建绿主库并完成升级,期间产生了 5ca50a2f:1-16
  2. 绿只读副本是在这之后才基于绿主库的快照创建的。它的 binlog 从创建那一刻才开始记,创建之前发生的事务(包括这 16 个)通过快照进了它的gtid_executed,但从没写进过它的 binlog——一出生就躺在 gtid_purged 里。

切换后,Canal 带着蓝环境的 GTID 集合连接绿只读副本。服务端计算差集后发现需要补发 5ca50a2f:1-16,但这些事务不在可用 binlog 中,因此拒绝该请求。

这个结果与本文的接入方式和实例创建时序有关:Canal 订阅的是只读副本,而所需 GTID 已存在于该副本的 gtid_executed 中、却不在它的可用 binlog 里。换成别的拓扑是否出现同样结果,应通过接入端的 gtid_executedgtid_purged 与 Canal 位点自行核对,不能直接照搬本文结论。

这里有两个容易误判的点:

  • binlog retention 不能消除这次 errno 1236,但仍然应该延长。 对本文的只读副本接入点而言,延长 binlog retention 不能补回从未写入该只读副本 binlog 的事务——5ca50a2f:1-16 在绿只读副本创建时通过快照进入了它的 gtid_executed,却从未写进它的 binlog。retention 决定的是故障处置窗口:只有仍保留在接入端 binlog 中的业务事务,才能在位点回拨后重新由 Canal 获取。
  • 回退 Canal 位点不能绕过集合校验。 缺失事务不在可用 binlog 中,扩大差集只会让服务端继续拒绝请求。

4.3 两种 GTID 位点恢复方式及其数据完整性影响

先明确一件事:Canal meta(file 模式下是 conf/<destination>/meta.dat)里的 GTID 集合,用于声明 Canal 已确认处理的事务范围。服务端按自身可用事务集合与该集合的差值决定要发送哪些事务;对于服务端不认识的外来 UUID,服务端也不会要求 Canal 回补该 UUID 对应的事务。所以恢复操作的本质是改写这个集合,改法直接决定数据完整性。

演练中最初的处理是:在绿只读副本上取当前位点与 Executed_Gtid_Set,整体写入 Canal meta 后重启。连接立刻恢复,日志干净,监控全绿:

WARN c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - ---> find start position successfully,
EntryPosition[included=false, journalName=mysql-bin-changelog.000078, position=4366322,
gtid=5ca50a2f-...:1-11347, a9fa6801-...:1-876334, c00d4e46-...:1-8, 1540d7c2-...:1-76910]
cost : 0ms , the next step is binlog dump

但代价藏在 GTID 集合里。覆盖的时候,绿主库的业务写入已经进行了约 25 分钟,5ca50a2f 涨到了 1-11347。整体覆盖等于在”已确认处理”的范围里补记了 1.1 万笔从未投递过的事务,服务端于是从 11348 开始发,17-11347 这段切换后的业务变更在 Kafka 里留下缺口。

使用当前 Executed_Gtid_Set 整体覆盖 Canal meta,会把切换后已在源端执行、但尚未由 Canal 投递的事务标记为已消费,造成下游数据缺口。Canal 连接恢复后未必立即暴露为投递错误,因此属于静默丢数据风险。

4.3.1 正确方式:并集修补

Canal 新 meta 的 GTID 集合 = Canal 旧 meta 的 GTID 集合 ∪ 绿环境的 gtid_purged

思路是只把确实补不回来的内部事务标成已消费,其余全部交给服务端照常补发。修补后的 GTID 集合只额外包含绿只读副本 gtid_purged 里无法从这个接入点获取的事务(通常就是 5ca50a2f:1-16 这样的小区间),其余未包含在 Canal 位点中的 GTID 仍由服务端按集合差集发送。

这一步的依据是第一章描述的差集补发行为,本文未保留修补后的对账输出。缺口是否补齐必须实际确认:用 GTID 差集核对 Canal 位点与接入端的 gtid_executed(Runbook 第 10 步),并对切换窗口做下游对账(第 11 步)。不要仅凭连接恢复、日志无报错和位点持续推进判断修补成功——前面的覆盖式恢复同样满足这三条。

这个公式还有个关键性质:不依赖执行时机。gtid_purged 里只有拿不回来的内部事务,切换后任何时间执行,结果都一样。反观整体覆盖,只有在切换写冻结窗口内执行才恰好正确,晚执行多久,就丢多久的数据。

4.3.2 覆盖已经发生时的补救

如果已经用当前 Executed_Gtid_Set 覆盖了 Canal 位点,而缺口对应的事务仍保留在绿只读副本的 binlog 中,可以通过回拨位点恢复。

停止 Canal instance 后,把 meta 中的 GTID 集合恢复为「原 Canal 位点 ∪ 绿只读副本的 gtid_purged」,再启动 instance。回拨前先在绿只读副本上检查缺口区间是否已被标记为 purged:

SELECT GTID_SUBTRACT(
  '5ca50a2f-...:17-11347',
  @@global.gtid_purged
);

返回完整区间,说明未发现该区间落入 gtid_purged;返回为空,则说明它已整体落入 gtid_purged,无法再从这个接入点获取。注意这只是必要条件——区间不在 gtid_purged 中,并不等于一定能补齐,仍需结合接入端实际的 binlog 可用性与 Canal 启动后的寻位日志确认。

4.3.3 回拨的代价与下游要求

回拨 Canal 位点后,已经写入 Kafka 的部分事务可能被再次投递。Canal 到 Kafka 的链路应按至少一次投递处理,下游需要能够按业务主键、事件 ID 或 GTID 实现幂等。

如果下游无法容忍重复消息,可以用独立 destination 和旁路 topic 回灌缺口区间,再由下游按既定的去重或合并规则处理。

4.4 位点存储与 ZooKeeper 模式下的修补

前文的修补操作以 file 模式(meta.dat)为例。实际部署中位点存在哪里,由 canal.properties 里的 canal.instance.global.spring.xml 决定。本文涉及的两种持久化模式:

配置值 位点存储位置 持久化行为 典型场景
file-instance.xml 本地文件 conf/<destination>/meta.dat 运行中的位点异步持久化到文件 单机部署
default-instance.xml ZooKeeper 运行中的位点异步持久化到 ZK 集群/HA 部署(依赖 canal.zkServers

memory-instance.xml 不持久化位点,重启后无法按本文方法读取和修补已有消费进度,因此不在本文讨论范围内。

不管哪种模式,动手修补前都得知道一条寻位优先级规则:instance 启动时,存量位点(meta/cursor)的优先级高于 instance.propertiescanal.instance.master.gtid 的配置值。只改配置、不清存量位点,新起点会被静默忽略,Canal 照旧从旧位点发起 dump。

位点修补的标准流程由此确定。先停止 instance 并备份现有 meta 或 cursor,随后选择以下一种方式:

  • 直接改写存量位点中的 GTID 集合;或
  • 删除存量位点,并在 instance.properties 中配置修补后的 canal.instance.master.gtid。

两种方式都应在启动前复核最终的 GTID 集合,启动后验证 Canal 的寻位日志与位点推进情况。注意这是二选一:只有走第二种方式才需要删除存量位点,直接改写时删除节点并非必经步骤。本节后面的 ZooKeeper 命令给出的是第二种方式的具体操作。

4.4.1 ZooKeeper 模式下查找位点

位点(cursor)节点的路径规则为:

/otter/canal/destinations/<destination>/<clientId>/cursor

本次演练的部署中,直投 MQ 的 destination cursor 位于 /otter/canal/destinations/<destination>/1001/cursor,其中 clientId 为 1001。其他 Canal 版本、客户端接入模式或定制配置下 client ID 可能不同,路径不存在时先列出 <destination> 的子节点确认实际值。两条与修补直接相关的读取命令:

get /otter/canal/destinations/<destination>/<clientId>/cursor   # 位点内容
get /otter/canal/destinations/<destination>/running             # 当前由哪台 server 运行(HA 归属)

两个容易踩空的细节:

  • chroot 前缀。如果 canal.zkServers 的值在端口后面带路径(比如 zk-host:2181/xxx),Canal 的全部节点都建在这个前缀下面,从 ZK 根路径看,实际位置是 /xxx/otter/canal/...。按 /otter/... 查不到节点时,先回头看看 canal.zkServers 有没有 chroot。
  • 读取的时效性。位点是异步持久化的,读到的 cursor 可能落后于内存中的实际处理进度,落后多少不由单一配置项决定——网络延迟、GC、写入失败都会影响它的可见时间。日常巡检可以直接读持久化位点;但作为修补输入时,应该先停止 instance 再读,那才是精确终值。

cursor 内容是一行 JSON(已脱敏,实际存储为单行,此处折行仅为便于阅读):

{"@type":"com.alibaba.otter.canal.protocol.position.LogPosition",
 "identity":{"slaveId":-1,"sourceAddress":{"address":"mysql8-test-ro.********.us-west-2.rds.amazonaws.com","port":3306}},
 "postion":{"gtid":"a9fa6801-...:1-876334,1540d7c2-...:1-76910",
            "included":false,"journalName":"mysql-bin-changelog.000078",
            "position":4366322,"serverId":1234567890,"timestamp":1765420941000}}

gtid 字段就是 3.3 节说的”已确认处理的事务范围”,也就是修补的对象;journalName/position 在 GTID 模式下仅作参考。留意一下 postion 这个词——它是 Canal 源码里的既有拼写,少一个 i,写解析或修补脚本时得按这个拼写取值。

4.4.2 ZooKeeper 模式下更新位点

推荐”删除节点 + 配置接管”的组合,避免手工拼装 JSON:

1. 停止 instance(Admin 托管部署在 Canal Admin 控制台操作,配置也在控制台修改)
2. get 位点节点,原样存档
3. 删除位点节点:
   deleteall /otter/canal/destinations/<destination>/1001/cursor   # ZK 3.4 用 rmr
4. 将并集写入 instance.properties:
   canal.instance.master.gtid = <旧 meta 的 GTID 集合 ∪ 绿只读副本的 gtid_purged>
5. 启动 instance:无存量位点时,Canal 按配置的 GTID 集合发起 dump

第 4 步有两个格式要求:值必须是单行完整集合(properties 文件里换行就意味着值结束);全文只保留一处 canal.instance.master.gtid,避免配置覆盖歧义(残留的旧配置行可能压掉新值)——改完启动后用寻位日志核对 Canal 实际使用的 GTID 起点。并集不用手工归并,随便找台 MySQL 8.0 实例就能规范化生成:

SELECT GTID_SUBTRACT(CONCAT('<旧 meta 的 GTID 集合>', ',', '<gtid_purged>'), '');

另一条路是前述第一种方式:用 set 直接改写 cursor JSON 里 gtid 字段的值。这个办法不依赖配置文件,但改写后的 JSON 必须保持单行、不含空格(zkCli 按空白切分参数),而且 set 完务必 get 复核,内容和预期逐字一致了再启动实例。

两种方式的验收标准一样:日志出现 find start position successfully 且起点为并集、无 errno 1236;启动一分钟后再 get 一次 cursor,gtid 集合应该在并集基础上持续推进。

五、追赶阶段的连带问题:Kafka 消息尺寸三处检查

位点修复后 Canal 进入全速追赶,随即触发第二类问题:

ERROR c.a.o.canal.connector.kafka.producer.CanalKafkaProducer -
org.apache.kafka.common.errors.RecordTooLargeException:
The message is 2480478 bytes when serialized which is larger than the maximum request size
you have configured with the max.request.size configuration.

原因是追赶期的打包放大:Canal 默认把最多 50 个 entry(canal.mq.canalBatchSize = 50)打进一条 Kafka 消息。稳态流量下攒不满、消息小;追赶期批批打满,再叠加大字段的行变更,消息轻松突破 1MB 默认限制。是否触发取决于 canal.mq.canalBatchSize、单行或大字段变更的大小、序列化格式、压缩配置,以及 producer、broker/topic 与 consumer 三处的消息大小限制。只要追赶期批次能够打满、且多个 entry 聚合后的序列化大小超过 kafka.max.request.size,producer 就会报 RecordTooLargeException——本次演练中触发它的那条消息是 2,480,478 字节。这几项配置应在切换前调好。

同一个大小限制在链路上 canal producer, kafka topic/broker 和下游 consumer 要一起调。只调一处,错误只是换个位置再冒出来(报错文案会从 max.request.size 变成 the max message size the server will accept):

闸口 配置项 位置
Canal producer kafka.max.request.size canal.properties,改后重启 Canal Server
Kafka topic/broker max.message.bytes(topic 级)或 message.max.bytes(broker 级) kafka-configs.sh --alter,动态生效
下游 consumer max.partition.fetch.bytes 消费端配置

实操中还踩到三个细节,都值得写进 Runbook:

  1. MSK 的 topic 参数建议用 kafka-configs.sh 增量修改,改完用 –-describe --all 验证 max.message.bytes 的实际生效值;
  2. 确认消息实际写到了哪个 topic。如果 instance.properties 配了 canal.mq.dynamicTopic,Canal 会按库/表路由到多个 topic,得全部调整,或者干脆调 broker 级默认值;
  3. 发送失败可能让 Canal 的投递线程退出。本次演练中 RuntimeException 穿透线程池后,该 destination 的投递线程终止,parser 继续读到内存 ring buffer(默认 16384)填满才阻塞。
    • 表现:源端位点持续前进、Canal 位点静止,日志里也可能不再出现新的投递异常。「源端在写而 Canal 位点不动」应作为切换后的监控项,别只依赖错误日志。
    • 处置:修复三处尺寸限制后重启对应 instance。重启前存档 Canal 位点与 Kafka topic offset;本次演练中重启后 Canal 从持久化位点重读,未确认的失败批次被重新处理,是否补齐由下游对账确认。

压缩(kafka.compression.type)不能规避 producer 侧的这道检查。按 Kafka producer 配置文档的说明,max.request.size 实际上是对未压缩 record batch 大小的上限,因此仅调整压缩配置不足以消除 RecordTooLargeException

六、生产切割 Runbook

以下清单适用于本文演练的 RDS 蓝绿切换与 Canal GTID 消费拓扑。若 Canal 订阅主库、使用 file/position 模式、位点存储机制不同,或下游并非 Kafka,应重新评估位点修补和验证步骤。

切割前(绿环境创建后)

1. 确认 Canal 为 canal-1.1.8 正式版或更高(1.1.8 的 alpha-1、alpha-2 不含 8.4 语法适配,见第一章)、复制账号为 caching_sha2_passwordgtidon = true、连接使用 DNS endpoint;

2. 在创建绿环境之前设置 binlog retention,绿环境建成后在绿只读副本上 CALL mysql.rds_show_configuration; 验证配置值。保留时间应覆盖变更窗口、故障诊断和必要的回拨时间;本次演练取 RDS for MySQL 的上限:CALL mysql.rds_set_configuration('binlog retention hours', 168);

3. 在绿只读副本上记录 SELECT @@global.gtid_executed, @@global.gtid_purged;gtid_purged 即切换后待并入 Canal meta 的区间;

4. 预先调整 Kafka 三道闸(producer / topic / consumer 尺寸上限),按业务最大单行变更峰值留足余量;确认 dynamicTopic 涉及的全部 topic;

5. 确认位点存储模式(canal.instance.global.spring.xml),并按 3.4 节预先演练对应的位点修补流程;留下现有 cursor/meta 的备份、修补后的 GTID 集合和恢复命令。

切割时

6. 执行 switchover(RDS 冻结写入)。RDS 文档给出的停机时间是通常少于一分钟,实际时长取决于工作负载;

7. Canal 连接绿只读副本时收到 errno 1236 属于预期现象。停止对应 instance,把切换前记录的 Canal GTID 位点与第 3 步记录的 gtid_purged 求并集(不是替换),按 3.4 节更新或重建位点,再启动 instance。不要通过反复重连或回退位点尝试恢复,也不要用当前 Executed_Gtid_Set 覆盖存量位点;

8. 验证:日志出现 find start position successfully 且无 1236、GTID 位点持续前进、追赶期无 RecordTooLarge。

切割后

9. 在源库写入 marker 数据,确认它出现在对应的 Kafka topic 中,以此验证链路端到端连通;

10. 用 GTID_SUBTRACT(只读副本的 gtid_executed, Canal meta 的 gtid) 确认差值仅剩内部事务区间;

11. 对切换窗口做下游抽样对账;观察期结束后清理蓝环境。

七、总结

本次演练观察到该拓扑下 Canal 在蓝绿切换前后的位点衔接与追赶阶段行为。几条核心结论:

  1. GTID 模式使 Canal 可以跨实例表达消费进度,但不能保证新接入端具备补发差集中所需事务的 binlog;
  2. 位点恢复应将 Canal 原有 GTID 位点与接入端的 gtid_purged 求并集;不要用当前实例的 Executed_Gtid_Set 覆盖 Canal 位点;
  3. 切换预案还应包括 Canal producer、Kafka topic/broker 与下游 consumer 的消息大小限制,以及修改后实际生效值的验证;
  4. 本文未验证 Canal 订阅主库、非 GTID 模式消费、跨区域复制、其他位点存储后端以及非 Kafka 下游的行为。将本文步骤用于生产前,应在与目标环境一致的拓扑中验证位点修补、追赶期间的吞吐能力和下游数据对账流程。

➡️ 下一步行动:

相关产品:

相关文章:

八、参考资料

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

陈浩

好未来DBA,负责好未来自建数据库和多云厂商云产品的运维工作,热衷于构造提效和解决问题的工具及系统,愿望是达成人、系统和数据库的生态和谐。

尹帮友

好未来 SRE 工程师,主要负责国内外智能硬件业务,深度结合 AI 落地智能运维体系。专注于构建高可靠、高弹性的软硬件一体化运维架构,在保障海外国内业务稳定性的同时优化运维成本,推动 AIOps 能力落地与团队技术沉淀创新,助力业务在复杂环境下高质量发展。

冯源

亚马逊解决方案架构师,曾服务于南洋理工大学、惠普和新智云。工作涉及企业混合云环境运维管理、运营管理、混合云平台和云服务解决方案。有十余年企业基础设施咨询及实施、项目管理和交付以及混合云平台研发管理经验。2022年加入AWS,负责零售、餐饮和制造等行业客户。现阶段在认真学习和熟悉AWS服务和产品,关注混合云及混合云环境下的应用部署和管理。


AWS 架构师中心:云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用