MySQL表级增量同步必须基于ROW格式binlog,配合table_id解析与位点连续维护实现;需启用binlog_row_image=FULL、log_slave_updates=ON等参数,并通过应用层过滤TABLE_MAP_EVENT事件,而非丢弃非目标表事件。

MySQL 表级增量同步不能靠 mysqldump 或全量 SELECT 实现,必须走 binlog;但直接解析 binlog 有门槛,且官方不提供“只订阅某几张表”的原生接口——得靠过滤策略 + 正确的 binlog 格式 + 中继日志(relay log)协同控制范围和时序。
确认 binlog 格式为 ROW 且开启必要参数
STATEMENT 或 MIXED 模式下无法精准捕获行变更,binlog_format=ROW 是表级增量同步的前提。否则你看到的可能是 UPDATE t1 SET x=1 这种语句级记录,根本不知道哪几行被改了。
还需确保以下配置已启用:
-
binlog_row_image=FULL(避免部分更新丢失旧值) -
log_slave_updates=ON(中继日志才能写入 relay log,供下游消费) -
server_id唯一且非 0(主从识别基础) - 若用 GTID,需
gtid_mode=ON+enforce_gtid_consistency=ON(提升位置定位可靠性)
用 mysqlbinlog 工具做轻量级表过滤解析
mysqlbinlog 本身不支持 SQL 层面的表名过滤,但可通过解析输出后 grep + 解析事件头来筛出目标表。关键点在于识别 Table_map_log_event 和后续的 Write_rows_log_event/Update_rows_log_event。
示例:只提取 db1.t_user 的变更
mysqlbinlog --base64-output=DECODE-ROWS -v \
--database=db1 \
/var/lib/mysql/mysql-bin.000001 | \
grep -A 20 -B 5 "table_id: [0-9]*" | \
awk '/^#.*at [0-9]+$/ {p=$0} /db1\.t_user/ {print p; getline; print}'
注意:--database 只过滤 USE db 语句,不能替代表级过滤;真正可靠的方式是解析 event header 中的 table_id 并关联 TABLE_MAP_EVENT 映射表名——这需要额外解析逻辑,纯 shell 不够健壮。
基于 MySQL 复制协议实现可控订阅(推荐方案)
绕过文件解析,直接连接 MySQL 主库,用 COM_BINLOG_DUMP_GTID 或 COM_BINLOG_DUMP 协议拉取 binlog 流,再在客户端按 table_map 事件匹配目标表。主流工具如 canal、maxwell、debezium 都走这条路。
关键控制点:
- 在启动复制客户端时,设置
replicate_do_table=db1.t_user(仅对从库有效,不适用于直连主库的订阅客户端) - 真正生效的过滤必须在应用层:收到
TABLE_MAP_EVENT后查表名,只处理白名单内的WriteRows/UpdateRows/DeleteRows - 中继日志(relay log)在此场景中主要用于构建高可用链路:例如 A → B(中继)→ C,B 开启
log_slave_updates,C 就能从 B 的 relay log 继续订阅,避免直连主库压力过大
忽略 GTID 或 position 跳变会导致数据错乱
表级过滤不是“跳过不关心的事件”,而是“跳过不关心的事件但保留位点推进”。如果只解析 t_user 事件却把中间其他表的 QUERY_EVENT 或 XID_EVENT 全丢弃,GTID set 或 binlog position 就会断层,下次重连可能漏数据或重复消费。
正确做法:
- 始终顺序遍历所有事件,维护当前
Executed_Gtid_Set或binlog filename/position - 对非目标表事件,只跳过业务处理,仍执行位点更新逻辑
- 若使用
relay log,需确保Relay_Log_File和Relay_Log_Pos与主库 binlog 严格对应,否则CHANGE MASTER TO ... RELAY_LOG_FILE会失败
最易被忽略的是:relay log 文件名默认不带主机标识,多实例共用目录时容易混淆;务必通过 relay_log_basename 显式隔离路径。











