问题和制约

数据库的双主双写并双向同步场景主要考虑数据完整性一致性和避免冲突对于同一个库同一张表同一个记录中的同一字段的两地变更会引发数据一致性判断冲突尽可能通过业务场景设计规避双主双写并同步复制可能引发主键冲突需避免使用数据库自增类主键方案另外双向同步潜在可能引发循环同步的问题需要做回环控制


原生 Dual Master 方案

MySQL 自身支持双主配置但并没有去解决潜在的主键和双写带来的数据一致性冲突对于双向同步潜在的循环复制问题MySQL 在 binlog 中记录了当前 MySQL 的 serverid一旦有了 serverid 的值之后MySQL 就很容易判断某个变更是从哪一个 Server 最初产生的所以就很容易避免出现循环复制的情况而且还可以配置不打开记录 slave 的 binlog 选项(logslaveupdate)MySQL 就不会记录复制过程中的变更到 binlog 中就更不用担心可能会出现循环复制的情形了


从 MySQL 自身的方案中可以找到切入点就是如果能在 binlog 中打上标记就有办法判断哪些 binlog 是复制产生的并将其过滤使用 MySQL 的方案则过于耦合 MySQL 的配置在大规模部署的线上生产系统中容易因为 MySQL 配置错误导致问题


自定义标记 SQL 方案

为了和 MySQL 配置解耦合可以考虑一种通用的标记 SQL 方案简单来说就是在同步复制入库时插入特殊的标记 SQL 语句来标记这是来自复制程序的变更这个标记 SQL 会进入 binlog 中而在复制程序读取时通过识别这个标记 SQL 来过滤判断binlog 中存储了对数据产生变更影响的的 SQL 语句这些 SQL 语句组成了一段一段的事务如下图所示

1.png

绿色区是业务运行产生的正常事务红色区是复制程序写入产生的事务其中蓝色块是标记 SQL标记 SQL 分别在事务开始后与事务结束前标记 SQL 更新一张预定义的区别于业务表的标记表那么每次复制程序去批量读取 binlog 内容时可能存在下面  种情况 如下图所示  

2.png

批量读取范围全落在绿色区内

批量读取范围起点落在绿色区终点落在红色区

批量读取范围起点落在红色区终点落在绿色区

批量读取范围起点和终点都在绿色区但中间涵盖了一段红色区

批量读取范围全落在红色区


如上只有第  种情况一个事务被拆成  段来同步中间一段因为没有事务头和尾的标记复制程序读取时将无法判断导致循环同步需要避免通过把复制程序的批量读取范围固定设置为至少大于或等于写入的事务长度范围避免了第  种情况复制程序批量读取 binlog 日志事件时通过标记 SQL 来过滤避免了循环复制实现了回环控制


总结

本文考虑了在 MySQL 双主写入场景下双向同步复制的一些设计要点和制约以原生实现为参考给出了一种自定义实现方式的设计要点分析而对于同库同表同记录同字段的同时两地变更则必然引发数据一致性冲突在复制同步层面无法区分哪边的更新为准通常会考虑以最后时间戳来恢复到一致状态但时间戳实际也会产生误差此类场景不多见最好还是尽可能还是在业务场景设计上来规避