TL;DR
- 场景:需要实时同步 MySQL 数据变更到下游系统(Kafka、ES、HDFS 等)
- 结论:Canal 通过模拟 MySQL 从库协议,解析 binlog 实现增量数据捕获
- 产出:完整的 Canal 工作原理与 MySQL binlog 配置指南
版本矩阵
| 功能 | 状态 | 说明 |
|---|---|---|
| Binlog 解析 | ✅ 已验证 | 支持 ROW/STATEMENT/MIXED 格式解析 |
| 从库协议模拟 | ✅ 已验证 | COM_REGISTER_SLAVE + COM_BINLOG_DUMP |
| Event/Entry 模型 | ✅ 已验证 | 标准化的数据变更事件封装 |
| 增量订阅消费 | ✅ 已验证 | 支持 Kafka/RocketMQ/HDFS/ES 等下游 |
| 断点续传 | ⚠️ 待验证 | 依赖位点记录,需实际环境测试 |
工作原理
Canal 是一款用于 MySQL 数据库 binlog 增量订阅和消费的开源工具。它主要用于解决数据同步和分布式事务问题,支持将数据库变更同步到其他系统中,比如消息队列、大数据平台等。
复制过程分为三步:
- Master 主库将改变记录写进二进制 binary log 中
- Slave 从库向 MySQL Master 发送 DUMP 协议,将 Master 主库的 binary log events 拷贝到它的中继日志(relay log)。
- Slave 从库读取并重做中继日志中的事件,将改变的数据同步到自己的数据库
核心概念
Binlog (Binary Log)
MySQL 的二进制日志,记录数据库中数据变更的所有事件,如 INSERT、UPDATE、DELETE 等操作。Canal 通过解析 binlog 来捕获这些数据变更。
主从复制机制
MySQL 支持主从复制,主库将 binlog 传输到从库,从库根据 binlog 重放操作,实现数据同步。Canal 模拟 MySQL 从库的行为,通过伪装从库来接收 binlog 数据。
Dump 和 Parse
- Dump 阶段:通过模拟 MySQL Slave 的协议,获取 binlog 数据流。
- Parse 阶段:对 binlog 数据进行解析,转换为可消费的数据结构。
Event 与 Entry 模型
Canal 会将 binlog 数据转换为事件流(Event),包含具体的 SQL 操作及影响的表和数据内容。
工作流程
伪装 Slave,连接 Master
Canal 模拟 MySQL 从库,执行 COM_REGISTER_SLAVE 命令向主库注册,并请求从某个位置开始拉取 binlog。连接过程类似于 MySQL 主从复制协议。
拉取 Binlog 数据
Canal 发送 BINLOG_DUMP 命令,从主库获取 binlog 日志流。
解析 Binlog 数据
Canal 接收到二进制日志后,会按照 MySQL binlog 格式进行解析,将其还原为 SQL 操作或数据变更事件。
格式化 Event 数据
Canal 会将解析出的 binlog 数据转换为内部的 Event 对象。Event 包含表名、主键、操作类型(插入、更新、删除)以及具体的数据内容。
数据投递与消费
Canal 将解析后的 Event 提供给消费端,如 Kafka、RocketMQ 等消息队列或直接写入 HDFS、Elasticsearch 等存储系统。消费端可以根据需要进行实时处理或分析。
数据流详细过程
模拟 Slave 同步 binlog
- Canal 模拟 Slave 发送 COM_BINLOG_DUMP 请求到主库。
- 主库接收到请求后,开始将 binlog 数据发送给 Canal。
- Canal 记录 binlog 的位点(position)信息,便于断点续传。
Binlog 数据解析
Canal 按照 MySQL 的 binlog 格式解析出各类事件:
- Query Event:SQL 语句事件,如 DDL 操作(CREATE TABLE 等)。
- Row Event:行级操作事件,具体包括:
- Write Rows (INSERT)
- Update Rows (UPDATE)
- Delete Rows (DELETE)
Canal 将这些事件转换为通用的 Entry 格式,并封装具体的行数据和元数据信息。
投递与消费处理
- Canal 提供的事件数据会被推送到消息队列(Kafka、RabbitMQ)或存储系统(HDFS、ElasticSearch)。
- 下游应用从这些队列或存储中获取数据,进行实时分析、同步、索引等操作。
Binlog 消息结构
Canal 将 binlog 转换为统一的消息格式,主要包括以下几部分:
- Header:描述消息的元信息,例如数据库名称、表名称、操作类型等。
- Row Data:记录表的行变更信息,包括主键和列数据(旧值、新值)。
- Transaction Info:支持事务的开始和结束标志,保证数据一致性。
MySQL Binlog
binlog 介绍
MySQL 的二进制日志可以说是 MySQL 最重要的日志了,它记录了所有的 DDL 和 DML(除了数据查询语句)语句,以事件形式记录,还包含语句执行的消耗时间,MySQL 的二进制日志是事物安全型的。
一般来说开启二进制日志会损耗 1% 的性能,二进制有两个最重要的场景:
- MySQL Replication:在 Master 端开启 Binlog,Master 把它的二进制日志传递给 Slaves 来到达 Master-Slave 数据一致的目的。
- 数据恢复:通过使用 MySQLBinlog 工具来使恢复数据。
二进制文件包括两类文件:二进制日志索引文件(文件后缀为 .index)用于记录所有的二进制文件,二进制日志文件(文件名后缀 .0000*)记录数据库所有的 DDL 和 DML(除了查询语句)语句事件。
binlog 开启
我们先打开配置文件:
vim /etc/my.cnf.d/mariadb-server.cnf
在 MySQL 的配置文件,在 [mysqld] 的区块下,添加:
log-bin=mysql-bin
这个表示 binlog 日志的前缀是 mysql-bin,以后生成的日志文件是 mysql-bin.123456 的文件,后面的数字按顺序生成。每次 MySQL 重启或者达到单个文件大小的阈值时,新生一个文件,按顺序编号。
退出保存后,重启 MySQL 服务:
systemctl restart mariadb
binlog 分类设置
MySQL binlog 的格式有三种,分别是:STATEMENT、ROW、MIXED。
statement
语句级,binlog 会记录每次执行写操作的语句。
相对 row 模式节省空间,但是可能产生不一致性,比如:update tt set create_date=now(),此时如果用 binlog 恢复,由于执行时间点不同,所以结果也不同。
- 优点:节省空间
- 缺点:有可能造成数据不一致
row
行级,binlog 会记录每次操作后每行记录的变化。
- 优点:保持数据的绝对一致性,因为不管 SQL 是什么,引用了什么函数,它只记录执行后的效果
- 缺点:占用较大空间
mixed
statement 的升级版本,一定程度上解决了因为一些情况而造成的 statement 模式不一致的问题。比如某些情况:
- 当函数中包含 UUID 时
- 包含 AUTO_INCREMENT 字段的表被更新时
- 执行 INSERT DELAYED 语句时
- 用 UDF 时
按照 ROW 的方式进行处理。
- 优点:节省空间,同时兼顾了一定的一致性
- 缺点:还有极个别情况依旧会造成不一致,另外 statement 和 mixed 对于需要 binlog 的监控的情况都不方便。
错误速查卡
| 症状 | 根因 | 定位 | 修复 |
|---|---|---|---|
| Canal 连接主库失败 | MySQL 未开启 binlog 或用户权限不足 | 检查 show master status 和 show grants for canal | 确保主库开启 binlog,并为 canal 用户授予 REPLICATION SLAVE 权限 |
| binlog 位点不存在 | 请求的 position 已被清理 | 查看 show master logs 确认可用范围 | 调整主库 expire_logs_days 或从最新位点开始消费 |
| 解析异常 Event | binlog 格式为混合模式且包含不可解析函数 | 检查 binlog 格式 show variables like 'binlog_format' | 建议生产环境使用 ROW 模式 |
| 数据不一致 | statement 模式下使用 now()/uuid() 等非确定性函数 | 对比 binlog 重放结果与源数据 | 切换为 ROW 模式 |
| 延迟过高 | 网络带宽不足或下游消费能力弱 | 监控 Canal metrics 和下游队列堆积 | 增加消费者实例或优化下游处理能力 |