cdc-go 在数据统计平台中的应用:从 binlog 到指标

chihqiang chihqiang 2026-11-14 约 12 分钟 4459 字

统计平台的数据问题,一半出在“同步”:要么同步慢了,要么同步漏了,要么同步错了

统计平台最典型的数据链路是:

1
业务库 → 同步 → 宽表/搜索索引 → 指标查询 → 看板

中间这一步“同步”,早期通常靠定时任务轮询:

1
SELECT * FROM `order` WHERE update_time > 上次时间;

轮询能跑,但有三个绕不开的问题:

  • 轮询间隔决定延迟,想快就得多查;
  • 查出来的数据不一定完整,订单的关联表可能还没更新;
  • 删除、状态变更、关联表变化,很难用一条 SQL 兜住。

cdc-go 解决的正是这一段:把 MySQL 的行级变更实时读出来,转成统一事件,投递到消息队列,由下游消费者决定怎么用。

本文不重复它的安装步骤,而是聊一个更实际的问题:在数据统计平台里,cdc-go 应该怎么用。


一、cdc-go 提供的是什么

cdc-go 是一个 Go 编写的轻量级 MySQL CDC 服务,Apache 2.0 开源。它的职责很单一:

1
MySQL binlog → cdc-go → Kafka / Redis / RabbitMQ / RocketMQ / Pulsar / stdout

它读取 ROW 格式的 binlog,把每一次行变更转换成一个统一的 JSON 事件,再发送到配置的输出端。位点保存在文件或 Redis 里,重启后从上次的位置继续,而不是从头重放。

几个和统计平台直接相关的特性:

  • 实时:按行变更捕获,不需要等定时任务;
  • 统一格式:insert / update / delete 都是同一种结构;
  • 位点续传:重启不丢进度;
  • 过滤表:用正则只订阅需要的库表;
  • 背压:下游慢的时候,读 binlog 的速度会跟着慢,而不是丢事件。

二、事件长什么样

event.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"time": "2026-10-21T10:31:15.482917+08:00",
"server_id": 1,
"pos": 194512512,
"row": {
"time": 1792549875,
"database": "shop",
"table": "order",
"type": "update",
"data": {
"id": 42,
"status": 2,
"actual_price": "199.00"
},
"old": {
"id": 42,
"status": 1,
"actual_price": "199.00"
}
}
}

字段含义:

字段说明
timecdc-go 捕获事件的时间
pos对应的 binlog 位点
row.timebinlog 事件时间,Unix 秒
row.database / row.table来源库表
row.typeinsert、update、delete
row.data变更后的整行;删除时是被删的行
row.old变更前的整行(只有 update 有)

注意 old 是整行,不是只包含变化的列。这个设计对下游很重要:它意味着你可以从事件里判断“哪个字段变了”,但不要指望它只给变化的字段。


三、在统计平台里的位置

把 cdc-go 放进统计平台的链路,大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
业务 MySQL
│ binlog
▼
cdc-go ──► Kafka topic
│
▼
消费者(任务注册)
│ 按 db.table 路由
▼
回业务库读当前完整状态
│
▼
拼宽文档 / 写搜索索引
│
▼
指标服务 → 看板 / 报表

这里有一个关键设计:事件只是“哪一行变了”的提示,文档内容以数据库当前状态为准。

为什么不直接用事件里的 data 拼文档?

因为统计用的宽文档通常跨很多张表:订单、商品、支付、退款、评价、渠道……当退款表变化时,事件里只有退款这一行,拼不出完整订单。回库读当前状态,才能保证:

  • 重复投递、乱序投递、补数重放,最终写入的都是当前真实状态;
  • 模型加字段时,不用改消费逻辑;
  • 出问题可以随时按主键重跑。

代价是每条变更要查一次库,对数据库有压力;这个取舍在统计场景是值得的——统计要的是“对”,不是“快一秒”。


四、消费侧怎么设计

cdc-go 只负责投递事件,消费侧要自己实现。统计平台的消费者,通常需要处理三类运行方式:

运行方式触发用途
实时消费Kafka 事件正常增量同步
按主键重跑手动 / 补偿修某一条数据
按时间区间重跑手动 / 定时补历史、兜底漏数

4.1 主表和子表

一个订单的宽文档,可能依赖十几张表。消费侧需要声明:

  • 主表:订单表,主键是 id;
  • 子表:退款、评价、商品明细等,通过 order_id 指向主表。

这样,无论变更来自主表还是子表,消费者都能算出“应该重写哪一条订单文档”。

4.2 幂等写入

事件可能重复(重投、补偿重叠),所以写入必须幂等:

  • 用“合并更新 + 冲突重试”写入文档;
  • 删除不物理删除,而是打一个“排除”标记,保证历史报表可重算;
  • 所有字段显式写入,空值也要写空,避免旧值残留。

4.3 补数

实时链路会漏事件:消费端重启、消息积压、下游限流。所以还需要一层兜底:

  • 按时间区间分页取主键,逐页重跑;
  • 时间列不能只看创建时间,支付、完成、退款时间都要纳入;
  • 补数和实时共用同一套拼装逻辑,保证结果一致。

五、实时与离线怎么配合

统计平台通常同时存在实时和离线两条链路:

1
2
实时:binlog → cdc-go → 消费者 → 宽文档 → 实时看板
离线:定时补数 / 全量重算 → 宽文档 → 日报、月报、结算

分工建议:

  • 实时负责“快”:看趋势、看异常、辅助决策;
  • 离线负责“准”:结算、考核、对外承诺;
  • 补数负责“兜底”:实时漏了、错了,按区间重跑。

三者共用同一份指标定义和同一套拼装逻辑,差异才可解释。


六、配置与部署要点

cdc-go 用一份 YAML 配置,分三段:位点存储、数据源、输出端。

config.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
store:
type: redis
redis:
addr: "127.0.0.1:6379"
db: 0

source:
type: mysql
mysql:
addr: "127.0.0.1:3306"
user: "cdcgo"
password: "${SOURCE_MYSQL_PASSWORD}"
server_id: 12345
exclude_table_regex:
- "^mysql\\."
- "^information_schema\\."

producer:
type: kafka
kafka:
brokers:
- "127.0.0.1:9092"
topic: "cdc-go-events"

几个容易踩的部署点:

  • server_id 必须唯一:不能和 MySQL 副本、其他 CDC 实例冲突;
  • 位点要持久化:容器环境用 Redis,不要用临时文件系统;
  • 只订阅需要的表:用 include/exclude 正则收窄范围,减少无关消息;
  • 密码走环境变量:配置支持 ${VAR} 和 ${VAR:-fallback},不要把密码写死;
  • 先看 stdout:本地验证时把 producer 设为 stdout,能直接看到事件结构。

七、常见坑

  1. 用事件里的 data 直接拼文档:跨表字段拼不出来,必须回库读;
  2. 按 row.time 当业务时间:它是 binlog 事件时间,不是订单创建/支付时间;
  3. 主键类型假设错误:事件里的主键可能是 int64、string 或 bytes,要按任务已知类型读取;
  4. 重复事件没去重:重投、补偿重叠都会造成重复写入;
  5. 删除物理删文档:历史报表重算会变,应该打排除标记;
  6. 只按创建时间补数:支付、退款、完成时间命中的单会漏;
  7. 位点存本地文件:容器重启后位点丢失,从头重放;
  8. server_id 冲突:多个 CDC 实例抢同一个 id,连接会被踢;
  9. 大事务:一次性产生大量事件,消费端要能扛住积压;
  10. DDL 变更:加字段后事件结构变化,消费端要兼容。

八、监控什么

cdc-go 把变更搬出来之后,统计平台还需要监控整条链路:

  • binlog 延迟:事件时间到当前时间的差距;
  • 消息积压:Kafka lag 是否持续增长;
  • 消费失败率:任务报错的比例;
  • 数据延迟:事件产生到文档可查的时间;
  • 补数进度:区间补数跑到哪、还剩多少。

只监控 cdc-go 本身不够,要监控“数据从产生到可查”的端到端延迟。


九、小结

cdc-go 解决的是“变更怎么搬出来”,统计平台要解决的是“搬出来之后怎么用”。

用一句话概括最佳实践:

事件只做提示,文档以库为准;实时负责快,补数负责全,指标负责准。

把这三件事分开,统计平台的数据链路才能既快又稳。


cdc-go 项目地址:https://github.com/chihqiang/cdc-go

分享: Twitter Facebook
chihqiang

chihqiang

代码与生活的点滴记录