如何用触发器 + binlog 实现简单的数据变更捕获

13次阅读

不能,触发器不记录历史变更且无法替代 binlog;binlog(需 ROW 模式)才是可靠、可重放的变更源,触发器仅作轻量实时响应或兜底补充。

如何用触发器 + binlog 实现简单的数据变更捕获

触发器能替代 binlog 吗?不能,但要一起用

触发器本身不记录历史变更,只在 DML 执行时同步响应;binlog 是 MySQL 的二进制日志,天然记录所有数据变更(需开启 binlog_format=ROW)。两者定位不同:触发器适合做轻量级实时响应(比如写入变更摘要表),binlog 才是可靠、可重放的变更源。想“捕获”而非“响应”,必须依赖 binlog,触发器只是辅助手段。

为什么 必须设为 binlog_format=ROW

MySQL 默认可能是 MIXEDSTATEMENT,这两种格式下 binlog 不保证每行变更都被记录——STATEMENT 只记 SQL 语句,MIXED 会自动降级,导致你解析不到具体哪几行被改了。只有 ROW 模式才确保 binlog event 包含 table_idbefore_imageafter_image,这是做精准 CDC 的基础。

  • 检查当前设置:SHOW VARIABLES LIKE 'binlog_format';
  • 修改需重启或动态设置(5.7.7+):SET GLOBAL binlog_format = 'ROW';,并确认已写入 配置文件 my.cnf[mysqld] 段落
  • 注意:ROW 模式会增大 binlog 体积,尤其批量更新时;但没有它,解析就不可靠

触发器怎么配合 binlog 做轻量兜底

binlog 解析需要额外服务(如 Debezium、Canal 或自研 parser),而触发器可以立刻写入一张 change_log 表,作为临时缓冲或审计补充。但它不能替代 binlog——因为触发器不记录 DDL、不捕获从库回放、无法处理主从切换后的位点对齐。

  • 示例:给 users 表加一个变更日志触发器
  • CREATE TRIGGER users_after_update AFTER UPDATE ON users FOR EACH ROW INSERT INTO change_log (table_name, pk_id, op_type, old_data, new_data, created_at) VALUES ('users', OLD.id, 'UPDATE', JSON_OBJECT('name', OLD.name), JSON_OBJECT('name', NEW.name), NOW());
  • 关键限制:OLD/NEW 只在当前事务可见,不能跨语句;大字段(如 TEXTBLOB)可能拖慢性能;触发器失效或报错会导致原 DML 失败(除非用 CONTAINS SQL + 异常捕获逻辑,但 MySQL 原生不支持 try-catch)

解析 binlog 时最容易忽略的三个坑

很多实操失败不是因为不会用 工具,而是忽略了 MySQL 底层机制。

  • server_id 必须全局唯一:同一集群中所有实例(包括从库)的 server_id 不能重复,否则 binlog event 会被跳过或错乱
  • 位点(filename + position)不是绝对安全的:MySQL 重启、binlog 切换、FLUSH LOGS 都会让 position 归零或跳变;生产环境必须用 GTID(gtid_mode=ON)来定位
  • 事务边界必须严格处理:一个 QUERY_EVENT 后可能跟多个 WRITE_ROWS_EVENT,它们属于同一个 XID_EVENT;漏掉事务头尾,就会把部分更新丢进黑洞

真正稳定的变更捕获,从来不是单点技巧的堆砌,而是触发器、binlog 格式、GTID、位点管理、事务解析这五者咬合运转的结果。少一个齿,链轮就打滑。

text=ZqhQzanResources