简介
引入数据复制的原因:
- 数据距离用户更近,让用户访问延迟降低
- 提高容错机制,某个节点坏掉后,其备份节点仍然能提供服务
- 负载扩展到多台机器,提高吞吐量
复制技术的真正难点在于如何处理持续变更的数据。
主节点与从节点
主从复制:
- 指定某个副本作为主副本,客户端全部写入主副本,更新主副本本地存储。
- 主副本把数据发送给从副本,从副本严格按照主副本发来的操作顺序,操作本地存储的数据
- 客户端可以从所有的副本中读取数据
同步复制: 主节点发送给从节点数据后,需要等从节点回复
异步复制: 主节点发送给从节点数据后,不必等从节点回复
同步复制,如果由于某些原因,导致主节点一直没有收到从节点的回复,那么后续的复制操作会阻塞。不过,同步复制逻辑简单,主节点发生故障后,任何一个从节点都可以当主节点。
异步复制,IO 非常高效,但是从节点会存在数据滞后的问题,主节点挂掉后,数据丢失。
半同步复制:有一个从节点保持和主节点同步操作,其余的从节点才用异步操作。相当于有个备份的主节点
配置新的从节点: 主节点定时 dump 数据快照,在下一次 dump 数据快照之前,保留更改的操作日志。新的从节点加入后,先发送数据快照,然后再发送更改日志即可。像 redis 之类的备份操作,都是这么做的。
实现复制日志:
- 基于语句的复制:复制
等更改语句,然后把对应的语句发送给从节点。实现简单,但是缺陷在于INSERT DEL
等与环境有关的命令没有意义。RNAD NOW
- 基于 WAL 预写日志:对于 SSTable 或者 LSMT 的方式,保留 追加的 write 日志,然后发送给从节点即可。适用于追加模式的数据库。
- 基于行的逻辑日志复制:类似于 MySQL 的 binlog,复制与存储分离。
复制滞后
复制滞后的问题,一般是暂时的,异步复制最终会一致,称之为『最终一致性』
**读自己的写:**写入主副本,但是读的从副本还没同步完数据
**单调读:**用户获取的数据的时间戳顺序不对。比如 A、B、C 3个对等机器,用户的请求路由到了3个机器上,A 中存在 x 用户的评论,但是 B C 中没有,A 返回了结果,B、C 也返回了结果,但是 B、C 的结果覆盖了 A 的结果,导致用户看到的评论消失了。
**前缀一致性:**用户获取的数据的时间戳是乱序的
多主节点
多个主节点,每个主节点同时是其他主节点的从节点。这些主节点,有自己的私有从节点。
场景:
- 多个数据中心同步数据
- 用户多个设备,比如一个用户有mac、iphone 和 ipad 这样的场景。这些设备可以离线
- 协作编辑
多主节点最难解决的是写入冲突的问题,一般来说最佳方案是避免冲突,其余方案这里暂时不涉及。
多主节点的拓扑结构:
- 环状拓扑
- 星式拓扑
-
p2p 模式
1和2的模式,最麻烦的地方在于关键路径损坏的话,会干扰整个集群的同步
无主节点
无主节点的方式,放弃主节点,允许客户端直接写入所有的结点。
客户端写入的时候,向多个副本写入数据。读取的时候,从多个副本读取。
- 读修复:假设某个节点下线后重新上线,其中的数据落后其他副本。则读取的时候,客户端会使用其他副本的数据,并更新当前落后节点的数据
- 反熵过程:客户端读取的时候,把其他副本中存在的数据,写入一个或者多个不存在数据的副本
假设 w 是一次性写入的副本个数,r 是一次性读取的副本的个数,n 是总的节点的个数,那么需要满足:w + r > n
w + r > n 通常可以配置,可以计算能容忍的坏节点的个数。
w + r 风格的局限性:
- 同时写入的情况,无法确认先后顺序
- 读写同时发生时,读的数据可能仍然是就值,因为写操作可能只是发生了一部分
- 某些副本写失败,整体操作认为失败,但是仍然有部分节点是成功的,此时读脏数据