Canal在ThinkPHP中实现数据同步的本质是伪装MySQL从库拉取binlog,TP仅消费其推送事件;需MySQL开启ROW格式binlog、授权canal用户REPLICATION权限,并用canal-php扩展配合进程保活与位点管理确保可靠性。

Canal 在 ThinkPHP 中做数据同步,本质不是 TP 主动“推”,而是 Canal 作为 MySQL 的伪从库拉取 binlog,TP 只负责消费 Canal 推送的变更事件。只要 Canal 服务正常、MySQL 配置正确、PHP 客户端能稳定连接 Canal server,同步就能持续运行——不依赖 TP 生命周期,也不需要改业务 SQL。
MySQL 必须开启 ROW 格式 binlog 并授权 canal 用户
Canal 解析的是 MySQL 的二进制日志,如果 binlog-format 不是 ROW,它就拿不到具体字段变更,只能看到语句(STATEMENT)或混合模式(MIXED),导致解析失败或漏数据。
- 检查是否开启:
SHOW VARIABLES LIKE 'log_bin';返回ON才行 - 确认格式:
SHOW VARIABLES LIKE 'binlog_format';必须是ROW - 修改
/etc/my.cnf(Linux)或my.ini(Windows)后重启 MySQL:
log-bin=mysql-bin binlog-format=ROW server-id=1
用户权限必须包含 REPLICATION SLAVE 和 REPLICATION CLIENT,仅 SELECT 不够:
CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';FLUSH PRIVILEGES;
ThinkPHP 8 命令行监听需用 canal-php 扩展并手动保活
TP8 自带的命令行调度器(php think command:run)不适用于长连接监听场景。canal-php 是阻塞式轮询 Canal server 的 get 接口,一旦连接断开或超时,进程就退出,不会自动重连。
立即学习“PHP免费学习笔记(深入)”;
- 安装扩展:
composer require xingwenge/canal_php:^1.0.3 - 创建命令类(如
app/command/CanalListener.php),核心逻辑是循环调用$client->get() - 不能直接在
handle()里写死 while(true),必须配合pcntl_fork()或 systemd/pm2 管理进程,否则 Ctrl+C 会杀不干净 - 建议用
nohup php think canal:listen > /dev/null 2>&1 &启动,并加简单心跳日志判断是否存活
canal-php 解析出的 Entry 数据结构容易误读字段名
canal-php 返回的 Entry 对象中,rowChange.getRows() 是变更前后的完整行数据,但字段顺序不等于建表顺序,且没有原始列名映射——它只按 binlog event 中 column meta 顺序返回值数组,列名需额外从 columnList 提取。
- 错误写法:
$row['id']——$row是索引数组,不是关联数组 - 正确做法:先遍历
$entry->getColumnList()拿到字段名列表,再用 array_combine 构造关联行数据 - UPDATE 类型有
beforeColumns和afterColumns,别混淆;DELETE 只有beforeColumns;INSERT 只有afterColumns - DDL 事件(如 ALTER TABLE)也会触发回调,但
rowChange为空,需单独判断$entry->getEventType()类型
生产环境必须处理 Canal server 断连和重复投递
Canal server 默认不保证 exactly-once 投递,网络抖动或 client 处理慢时,可能重复推送同一批 batchId,也可能因超时丢失部分 event。TP 侧不能假设“一次 get 就是一次可靠变更”。
- 必须记录已处理的
batchId或position(如写入 Redis 或本地文件),每次 get 前校验是否已处理过 - 避免用 MySQL 表存位点——TP 命令进程挂掉时事务未提交,位点就丢了
- 推荐用 Redis + Lua 原子写入位点:
SET canal:position:{destination} "{batchId}:{eventId}" EX 86400 NX - 遇到重复 batch,直接
continue跳过,不要抛异常中断整个监听流
真正难的从来不是“怎么连上 Canal”,而是如何让 PHP 进程在后台稳住、不丢 event、不重复处理、还能快速定位哪条 binlog 卡住了——这些细节没压测过,上线后第一周必出问题。



















