目录
1. 数据同步的推/拉方式
从数据同步如何触发的角度来看,有两种常见模式。根据实时性要求、系统规模、网络条件等进行选择。
1.1 主节点推送
当主节点数据变更时,主动向所有从节点推送新数据。
优点:
- 实时性好:主节点有数据变更时可以立即通知从节点,减少延迟。
- 负载均衡:主节点可以根据从节点的状态(如网络状况、处理能力等)分配任务。
- 降低从节点负担:从节点不需要定期轮询主节点,减少网络和CPU占用。
缺点:
- 复杂度高:需要维护一个可靠的通信机制,并且要处理可能出现的网络问题、同步丢失问题。
- 扩展性挑战:随着从节点数量的增加,主节点可能成为瓶颈。
1.2 从节点拉取
从节点定期询问主节点是否有数据更新,有则拉取新数据。
优点:
- 灵活性好:从节点轮询请求主节点,可根据自身情况调整拉取频率。
- 易扩展:新增从节点时,只需配置好拉取逻辑即可,对主节点的影响较小。
缺点:
- 存在延迟:取决于从节点设置的检查间隔,不能保证数据的即时一致性。
- 增加主节点压力:频繁的轮询会增加主节点的负载。
- 效率较低:即使没有新数据更新也需要发起查询请求,浪费资源。
1.3 常见组件的推拉方式
组件
|
同步方式
|
MySQL
|
从节点拉取(通过 binlog 实现)
|
Etcd
|
主节点推送(raft 协议同步数据) + 从节点拉取(心跳)
|
MongoDB副本集
|
从节点拉取(通过 oplog 实现)
|
Redis
|
从节点拉取(异步复制) + 主节点推送(首次全量同步)
|
ZooKeeper
|
主节点推送(ZAB 协议同步数据) + 从节点拉取(心跳)
|
2.复制方式
2.1 同步复制
leader 节点等待所有 follower 节点确认已接收到并处理完数据后,才执行后续操作。
优点:数据高度一致性
缺点:可能会导致性能瓶颈,特别是在网络延迟较高的情况下
适用场景:高一致性要求、分布式协调(如 etcd 的 raft 协议)
2.2 异步复制
leader 节点不需等待 follower 返回,直接继续后续操作。
优点:性能好,实时性高
缺点:数据同步有延迟,数据一致性较低
适用场景:日志分析、社交媒体等一致性要求不高的系统
2.3 半同步复制
介于同步复制和异步复制之间的一种折衷方案。在这种模式下,只要一个 follower 返回之后,leader 就可以进行下一步操作。
这种方式既保证了一定程度的数据一致性,又避免了完全同步复制带来的性能损耗。
适用场景:MySQL 半同步复制、MongoDB 的 w:majority 模式
注意:共识算法并不是半同步模式而是强同步模式,因为半同步只需 1 个节点返回即可,存在数据丢失风险;但是共识算法要求多数节点返回,是严格保证一致性的。
2.4 常见组件的同步方式
组件 | 默认同步方式 | 一致性级别 | 其他同步方式支持 |
MySQL | 异步 | 可调(最终→强) | 支持半同步(插件配置,需至少1个从节点确认), 组复制(多数节点同步) |
PostgreSQL | 同步(流复制) | 可调 | 可配置为异步(synchronous_commit=off), 支持同步提交(remote_apply严格同步) |
MongoDB | 异步 | 可调(w:1→w:majority) | 通过写关注(Write Concern)配置半同步(如w:majority+j:true) |
Redis | 异步 | 弱一致性 | 可使用WAIT命令,手动等待指定个数的节点复制完成,但本质还是异步的 |
Etcd | 同步(Raft) | 强一致性 | 不支持 |
Kafka | 异步(acks=1) | 可调 | acks=all(等待所有节点确认), min.insync.replicas控制最小同步副本数 |
比如查询 mysql binLog 的同步方式:
SHOW VARIABLES LIKE 'sync_binlog';
有三种配置:
- 0 - 不主动刷盘,由操作系统决定何时刷盘(安全性差,数据易丢失)
- 1 - 每次提交事务时刷盘(安全性高,性能差)(默认值)
- N - 每N次提交后刷盘(平衡安全性和性能)
3.日志格式
3.1 基于语句复制 SBR
基于语句的复制(SBR, Statement-Based Replication):leader 记录下它执行的每个语句(INSERT/UPDATE/DELETE)的日志,把日志发给 follower,每个 follower 解析并执行语句。
优点:生成的数据量小, 磁盘 IO 次数少,性能好
缺点:会导致 follower 上生成不同的数据(如使用 NOW()、RAND()、自增 ID、基于现有数据 UPDATE)
基于语句的复制在 mysql 5.1 之前使用,现在默认已经不再使用。
3.2 基于行复制 RBR
基于行的复制(RBR, Row-Based Replication):记录每行数据的变化,当数据被修改时,生成的日志包含被修改行的唯一标识,以及所有列的新值。
优点:由于复制的是原始数据,不会出现数据不一致的情况
缺点:数据量大,磁盘 IO、网络开销高
这种方式现在是 mysql binLog 默认的日志格式。可以通过命令查询:
SHOW VARIABLES LIKE 'binlog_format’;
有三种配置:
- Statement: 基于语句复制
- Row: 基于行复制(默认值)
- Mixed: Statement 与 Row 混合(默认使用 Statement,涉及日期、函数相关时用 Row)
可以看出 mysql binLog 和 redis 复制方式刚好对应,redis 也是三种:
- RDB:全量同步
- AOF:增量同步
- 混合模式
3.3 基于预写日志 WAL
预写日志(WAL, Write-Ahead Logging):在数据实际写入存储之前,先记录所有变更到日志(prepare状态),等待后台异步刷盘后,再将日志中的变更更新到数据文件(commit状态),所以 WAL 本质上是一种两阶段提交。
这种方式适用于大多数类型的数据库。如 mysql 的 redoLog,etcd 的 raft log,pg 的 WAL,都是用的预写日志方式。
注意:WAL 和 RBR/SBR 并不冲突,比如 mysql 就是两种协调工作的:
- binLog 使用 RBR/SBR,工作在 Server 层,用于确保数据在多个节点上的一致性
- redoLog 使用 WAL,工作在 InnoDB 引擎层,用于确保数据在单个节点上不丢失
关于 mysql 的几种日志可以看: (十一)MySQL日志篇,总结得相当之好了。
3.4 基于触发器复制
利用数据库的触发器功能,在特定事件发生时自动触发复制逻辑。虽然灵活性强,但由于依赖于数据库内部机制,维护成本较高。
4.不同复制架构下的同步机制
4.1 单主复制 Single Leader
只有一个主节点负责写操作,其余节点均为只读副本。
典型应用:mysql、pg、mongoDB副本集、etcd。
这是最常见也是最简单的复制模型,易于管理和维护,数据一致性强,但在高并发写入场景下可能存在瓶颈。
4.2 多主复制 Multi Leader
允许多个节点同时接受写操作。
典型应用:CouchDB、Cassandra、多活MySQL集群。
这种架构提高了系统的可用性和容错能力,但数据一致性较差,需要解决冲突。
冲突检测与解决机制:
- 最后写入胜利(LWW):基于时间戳选择最新值(可能丢数据)
- 客户端解决:应用层合并冲突(如Git的合并冲突)
- CRDTs(无冲突数据类型):数据结构本身支持自动合并(如计数器、集合)
4.3 无主复制 LeaderLess
没有明确的主节点概念,所有节点地位平等,均可接受读写请求。
典型应用:Dynamo、Cassandra、ScyllaDB。
此架构容错性高,非常适合分布式环境下的大规模部署,但同样数据一致性差。
常用技术:
- 读写仲裁(Quorum):写入时需成功W个节点,读取时需查询R个节点,满足W + R > N(N为副本总数)以保证一致性
- hinted handoff:节点不可达时,其他节点暂存数据并在恢复后转发
- 反熵(Anti-Entropy):后台进程定期同步节点间的差异数据
4.4 分片集群 Sharding
分片是一种水平拆分数据的架构模式,将数据集按特定规则(如哈希、范围)分布到多个节点上,每个节点只存储部分数据。
常见分片方式:
- 哈希分片: 对分片键(如用户ID)计算哈希值,按哈希范围分配数据。如 redis、mongoDB、es
- 范围分片: 按分片键的范围(如时间戳、ID区间)分配数据。如 mysql 分库分表
- 目录分片: 维护一个动态查找表,记录数据与分片的映射关系。如 zookeeper
分片通常与复制结合使用,构成分片+副本的混合架构:
- 分片层: 数据水平拆分到多个逻辑组
- 副本层: 每个分片由一个主节点和多个从节点组成
5.主从数据一致性问题
5.1 写后读一致性
场景:用户写入数据后立刻读取,读到了 follower 节点,此时这条数据尚未同步,导致读到旧数据。
解决:
- 如果是读用户自己的数据,则强制读主
- 写入时记录时间戳,读取时确保从节点的数据时间 ≥ 写入时间戳
- 写入后客户端将数据缓存,优先从缓存查询
5.2 单调读
场景: 同一用户连续多次读取时,可能看到数据回退(先读到新值,再读到旧值)。
解决:
- 会话粘滞: 同一用户的请求始终路由到同一个节点
- 客户端记录上次读取的版本号(时间戳),后续只接受 ≥ 该版本的数据
5.3 一致前缀读
场景:不同写入操作因网络乱序到达从库,导致读取到因果颠倒的数据。比如先看到 B 回复 A,再看到 A 的发言。
解决:
- 跟踪写入的因果依赖关系(如向量时钟),确保从库按顺序更新
- 使用全局有序日志(如共识算法),保证所有节点顺序写入
参考:
https://dev.mysql.com/doc/refman/8.0/en/replication-formats.html
(十一)MySQL日志篇之undo-log、redo-log、bin-log.....傻傻分不清!任何项目都会有日志,M - 掘金
(二十四)全解MySQL之主从篇:死磕主从复制中数据同步原理与优化主从复制是一种十分常见的高可用手段,MQ、Redis、 - 掘金