在MySQL主从架构里,主库把数据变更写成binlog,从库拉取并重放,从而保证两端一致。但实际运行中,从库往往只需要其中一部分数据,比如只关心订单库而非日志库。如果全量复制,从库会处理大量无用事件,浪费CPU、IO与网络资源。因此,我们有必要在主从复制链路中过滤掉不需要同步的内容,实现数据同步优化。

一、主库端的binlog过滤机制
主库可以通过系统变量控制哪些库的操作会写入二进制日志。最常用的两个参数是binlog_do_db和binlog_ignore_db。前者表示仅记录指定库的变更,后者表示忽略指定库的变更。它们是在会话当前默认数据库的基础上进行判断的,这一点非常容易踩坑。
例如,当使用基于语句的复制(STATEMENT)时,如果在连接中先执行USE test_db;再更新其他库,过滤规则可能不按预期生效。因此,主库过滤更适合在实例级别明确只服务单一业务库的场景。以下为配置示例:
-- 在 my.cnf 的 [mysqld] 段落中配置 -- 只记录 orders 库的 binlog binlog_do_db=orders -- 或者忽略临时统计库 binlog_ignore_db=report_tmp -- 查看当前生效的过滤规则 SHOW MASTER STATUS; SHOW VARIABLES LIKE 'binlog_do_db'; SHOW VARIABLES LIKE 'binlog_ignore_db';
需要注意,binlog_do_db和binlog_ignore_db是互斥且优先级分明的:如果设置了binlog_do_db,只有明确列出的库才会写binlog;未列出的库全部忽略。而binlog_ignore_db则是除了列出的库之外都记录。两者都不支持通配符,只能写具体库名。
由于主库过滤存在跨库会话的歧义问题,官方也更推荐在从库端做复制过滤,这样不论主库以什么方式产生binlog,从库都能按表名或库名精确决定是否重放。
二、从库端的复制过滤规则
从库提供了更细粒度且更安全的过滤参数,例如replicate_do_db、replicate_ignore_db、replicate_wild_do_table和replicate_wild_ignore_table。其中带有wild的表级参数支持百分号通配,非常适合按前缀批量排除日志表或临时表。
假设从库只需要同步orders库下的所有表,而完全不需要logs库,可以这样配置:
-- 在从库 my.cnf 中配置 replicate_wild_do_table=orders.% replicate_wild_ignore_table=logs.% -- 启动从库复制后查看过滤状态 SHOW SLAVE STATUSG
与replicate_do_db不同,replicate_wild_do_table是以实际变更的表名为依据进行匹配,不受USE语句影响,因此可靠性更高。在混合使用多个业务库的实例中,用表级通配过滤能精准避免不必要的复制。
此外,从MySQL 5.7之后支持动态修改部分复制过滤规则而无需重启,通过CHANGE REPLICATION FILTER语句即可在线调整,这对运维期优化同步范围非常友好。
-- 在线增加忽略规则
CHANGE REPLICATION FILTER REPLICATE_WILD_IGNORE_TABLE=('tmp.%');
三、binlog格式对同步优化的影响
MySQL支持STATEMENT、ROW和MIXED三种binlog格式。STATEMENT记录的是SQL语句本身,过滤时容易因上下文库名产生偏差;ROW格式记录每行实际数据变更,配合从库表级过滤最为稳妥,因为从库能清楚知道事件属于哪张表。
不过ROW格式会产生更大的binlog体积,如果不过滤,网络开销反而上升。所以优化策略通常是ROW格式加从库表级忽略规则,既保证准确又减少重放量。示例如下:
-- 主库设置 ROW 格式 SET GLOBAL binlog_format=ROW; -- 从库忽略统计相关表,减少重放 replicate_wild_ignore_table=stats.% replicate_wild_ignore_table=audit.%
为了避免ROW格式下排查困难,可开启binlog_rows_query_log_events,让binlog附带原始SQL文本,便于在过滤后仍可审计来源。但这会略微增加日志大小,需要权衡。
在MIXED模式下,MySQL自动在STATEMENT和ROW间切换,某些不确定性函数会转成ROW记录。如果使用了复制过滤,MIXED可能在某些边缘场景下导致从库漏放或错放,因此在有严格过滤需求时,明确使用ROW更可控。
四、通过业务拆分与通道隔离优化
除了参数过滤,架构层面的优化同样关键。如果多个业务强行共用一个MySQL实例,再怎么配置复制过滤,主库写binlog的压力也不会降低。更彻底的方案是按业务垂直分库,让只需要部分数据的从库直接挂载在对应业务主库下。
当暂时无法分库时,可以利用多源复制(multi-source replication)将不同业务的主库分别接到从库的不同复制通道,再对每个通道单独设置过滤,逻辑更清晰:
-- 配置多源复制通道并单独过滤
CHANGE MASTER TO MASTER_HOST='192.168.0.1',
MASTER_USER='repl', MASTER_PASSWORD='pwd'
FOR CHANNEL 'orders_channel';
CHANGE REPLICATION FILTER REPLICATE_WILD_DO_TABLE=('orders.%')
FOR CHANNEL 'orders_channel';
这种方式下,即使主库A和主库B都向同一个从库同步,从库也能通过通道级过滤只重放关心的数据。相比全局过滤,它降低了配置冲突风险,也方便后续单独监控每个业务的延迟。
从长期维护看,清晰的复制拓扑比复杂的过滤规则更重要。当团队规模扩大,显式拆分实例往往比在单一实例里写满replicate_wild_ignore_table更容易理解和排错。
五、常见误区与检查清单
一个典型误区是以为在主库设了binlog_ignore_db就万事大吉,结果从库还是收到相关事件。这是因为跨库更新时,若当前USE的是其他库,主库仍可能写入binlog。因此,检查时应直接解析binlog确认事件:
# 使用 mysqlbinlog 查看实际记录 mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000012 | grep -A5 'UPDATE'
另一个误区是在从库同时使用replicate_do_db和replicate_wild_do_table却理解错优先级,造成部分表未被重放。官方文档明确表级规则优先于库级规则,排查时应以SHOW SLAVE STATUS中的Replicate_Wild_Do_Table等字段为准。
最后,过滤规则变更后必须重启复制线程或重接通道才能生效,忘记这一步会导致配置写了却没用。建议每次调整后记录变更时间,并通过对比主从表行数或校验工具确认过滤符合预期,避免静默数据不一致。