DDIA 读书笔记(4)多节点数据复制方案
分布式系统的核心就是在节点失效、网络不可靠、副本一致性、持久性、可用性,与延迟之间权衡,每种方案都有自己的优劣,没有银弹。以下均为提出问题,不解决问题。
主从复制
主从方案是最简单的数据复制方案,所有的节点都保存了一份完整的数据,其中一个节点被指定为主节点,其余的均为从节点。客户端可以在任意节点读取数据,但是只能在主节点修改数据。
节点失效
从节点故障相对简单,恢复后追赶拉下的日志即可,主节点故障就麻烦了。需要将其中一个从节点提升为主节点,其他从节点和客户端读取新的主节点数据,这一过程充满了变数。
异步复制的情况下,主节点突然失效,从节点的数据可能会落后。经过一段时间原主节点重新上线,还会尝试同步其他的从节点,会出现数据冲突,常见的解决办法是丢弃原主节点上未同步的数据,但是这样丢失了持久化的承诺。
如果有其他的系统依赖数据库的内容,丢弃数据的办法很可能会导致数据不一致。
可能会出现两个节点都认为自己是主节点的情况(脑裂),非常的危险,数据冲突也不好解决。如果采取措施强制关闭其中一个节点,如果设计不当,也会出现两个节点都被关闭的情况。
配置主节点失效的超时时长是个很有学问的活,设的时间太长提高系统不可用的时间,时间太短可能会导致不必要的切换,反而因为频繁的应急操作影响系统稳定性。
复制滞后的问题
复制滞后是异步复制常见的现象,因为网络的延迟,从节点的数据通常会落后于主节点,有时候系统负载过高或网络延迟过高,这种滞后可能会达到几秒甚至几分钟。这种滞后会带来一些问题
应用刚写完主节点,随后去读从节点,因为滞后的关系,它读到了落后的数据。需要保证“写后读一致性”。
应用写完主节点,先读了一个同步完成的从节点,然后又读了一个滞后的从节点,出现了数据回滚的情况。需要保证“单调读一致性”。
应用读取数据时未指定顺序,可能会导致主从数据顺序不一致的情况,因为网络问题,从节点可能会先收到后写入数据的消息。需要“前缀一致读”保证
多主复制
主从复制有个明显的缺陷,就是主节点的单点问题,一但故障跟主节点相关,处理起来就会变得非常麻烦。基于主从模型拓展一下,就是配置多个主节点。相比主从模型,多主模型复杂度极高,除非是客户端的应用场景或是后端的多数据中心的灾备,否则 ROI 是不高的。
多主复制最大的问题在于可能会发生写冲突,当两个主节点都更改了同一行记录,并异步复制给对方时,就会发现冲突。因为两个写请求都是成功的,后续异步复制时才发现问题,因此没有办法交给用户层去解决
这一类的冲突没什么好的解决办法,比较理想的策略还是避免冲突,比如应用层可以保证特定数据的请求总是经过同一个主节点,这样对于应用层来说,基本等价于主从模型了。但有时候还是需要改变事先指定的主节点,比如数据中心故障,还是需要处理冲突问题。
无主复制
有些数据系统放弃了主节点的概念,集群中所有的节点都是可以读写的。这样的系统有两种实现方案,一种是客户端将请求发送给多个节点,另一种有个协调者负责代替客户端进行写入,和主节点略有不同,协调者是不负责写入的顺序的。
处理节点失效的情况
当一个节点失效后重新上线,它的数据是落后的,因此客户端读取数据时不应该依赖单个节点的数据,它需要请求多个节点,采用版本号最高的结果。
为了让落后的节点尽快赶上节奏,通常有两种机制来追赶数据。一种是读修复,客户端会同时读取多个节点的数据,因此可以检测到版本落后的节点,并对这些节点进行更新。还有一种方案是开一个后台进程不断地比对数据差异并修复,这个方案也是不保证写入顺序的。
读写 quorum
对于一个 n 节点的集群,写入需要 w 个节点确认,读取需要查询至少 r 个节点,那个理论上只要 w + r > n,就可以保证一定能读到最新的值。从可用性角度来讲,即是写入时可以容忍 n - w 个节点不可用,读取时可以容忍 n - r 个节点不可用。这是一种比较简单的分布式投票算法。对于一些可以容忍部分数据不一致的系统来说,可以将 w r 设定的宽松一些,使得 w + r <= n,这样可以提高吞吐量和稳定性
但是这个方案也不是完美的,还是会有很多问题需要处理:
如果两个写操作同时发生,系统没办法区分先后顺序,唯一安全的方案就是处理冲突。
如果写操作和读操作同时发生,读操作不一定读取到最新值,因为写操作并没有完成。
如果写操作失败了,但仍有一部分节点写入成功,这部分节点不会回滚,数据会不一致。
如果某些节点失效了,恢复的数据版本低于最新版本,读取仍可能会读到旧值。
还有一些其他边界情况后续再讨论。
版权声明: 本文为 InfoQ 作者【莫黎】的原创文章。
原文链接:【http://xie.infoq.cn/article/b9bb76300f43844c6a7c7c66e】。文章转载请联系作者。
评论