1. 一致性保证
关于最终一致性:多副本存储中,不一致现象是暂时的(但时长未知),最终会一样,即“收敛”。但它的一致性保证太弱,可以说基本没有。
关于弱保证:如read-after-write/read-your-write、monotonic reads等保证下,需要清楚了解系统局限性。
分布式一致性模型与事务隔离级别:相似,存在某些重叠,但也有显著区别:
- 前者:针对延迟和故障等问题,协调副本之间的状态
- 后者:处理并发执行事务时的各种临界条件
2. 可线性化——最强分布式一致性模型
可线性化思想:让一个系统看起来好像只有一个数据副本,且所有的操作都是原子的。
可线性化 = 原子一致性 = 强一致性…,CAP中的C即线性化
2.1. 如何达到可线性化
- 若写
write(x, v_new)
已完成,则完成后的所有的读(包括不同客户端)read(x)
必须读到v_new
- 若在写
write(x, v_new)
过程中,值会跳变,若其中的读read(x)
读到了新值v_new
,之后的所有读(包括不同客户端)必须返回v_new
根据返回结果,可推断操作可能执行的时间点,这些时间点相连,其指向不可能向后。
可线性化与可串行化的区别:
- 可线性化:单个操作,单个对象,实时顺序。它代表单个对象读写的最新值保证(因此无法避免幻读)。此外,可线性化的组合也是可线性化的。
- 可串行化:多个操作,多个对象,任意总顺序。它是事务的隔离属性。它确保事务执行的结果和某个串行执行结果相同即可(串行执行顺序可能和实际执行顺序不同)。但它不包含可线性化的“实时”概念。
可串行化和可线性化可以同时达到(严格可串行化),基于2PL或实际串行执行的实现都是可线性化的,而基于快照的可串行化是不可线性化的(因为从快照读肯定不是线性化)
2.2. 哪些场景需要线性化
2.2.1. 加锁与主节点选举
有些系统,集群中只能有一个主节点(否则脑裂),常见方法是使用锁(每个节点尝试获得锁,获得到的就是主节点)。
而锁的实现必须满足可线性化,即“所有节点必须知道哪个节点拥有锁,且结果一致”。
Paxos, Raft等共识算法可确保可线性化。
2.2.2. 约束与唯一性保证
数据有唯一性约束(如SQL的UNIQUE
约束)时,应该保证线性化(两个相同的写入应该一个返回错误),其和加锁本质相同。
其它场景的约束也需要所有节点就某个新值达成强一致(如银行账户等)。
2.2.3. 跨通道的时间依赖
当存在不同信道,且信道间存在竞争,如下图所示,文件存储需要保证线性化,才能保证第5,6步读到的是最新的图片。
也可以用更弱的等级,如read-after-write,但会增加额外复杂性。
2.3. 实现线性化系统
2.3.1. 不同复制方案下的可线性化
主从复制:若读取从主节点或者更新是同步的,可满足可线性化
共识算法:与主从复制机制相似,但通过一些措施防止脑裂和过期副本,安全实现可线性化
多主复制:通常无法线性化,因为副本在多节点上并发写入,且异步复制
无主复制:可能无法线性化,取决于quorum配置,宽松的quorum会违背线性化,甚至严格的quorum也会违背线性化
此外基于墙上时钟的LWW几乎肯定是非线性化,因为墙上时钟不能保证与实际事件顺序一致。
2.3.2. 线性化与严格quorum
通常,读写遵循严格的quorum,应该是可线性化的,然而在出现不确定的的网络延迟时,就会出现竞争条件,不满足线性化。
例子:$n=3, w=3, r=2$,时序如下图,第二次读不满足线性化。
解决方案:
- 读时进行同步的读修复
- 写前必须读取quorum节点获取最新值
但会显著降低性能,很少有采用。且这种方式不能支持线性化的“比较和设置”(CAS)操作(需要共识算法支持)。
2.4. 线性化的代价
网络中断(网络分区)迫使我们在可线性化和可用性中做出选择。
对于多主复制系统,不保证可线性化,但保证了可用性;
对于主从复制系统,若主从间网络中断,则:
- 若可用,从节点提供的数据是旧的,不满足可线性化
- 若满足可线性化,则需要主节点同步复制到从节点,所以必须返回错误给客户端,导致不可用
2.4.1. CAP理论
实际上任何一个数据中心内部,只要有不可靠的网络,就都会有违背线性化的风险,所以在满足出现网络故障时:
- 要求线性化,则必须等待网络修复,或之间返回错误——不可用
- 要求可用,则每个副本可独立处理请求——结果不满足线性化
CAP:C(线性一致性),A(可用),P(允许网络分区)之中只能满足2个。实际上P肯定存在,因此更准确的是:网络分区情况下,选择一致还是可用。
CAP应用范围较窄:只考虑一种一致性模型(线性化)和一种故障(网络分区,节点仍活动但相互断开)。
2.4.2. 不选择线性化的理由
线性化是一个有用的保证,但很少有系统选择支持它,因为它的读写性能非常差(原因在于网络延迟)。而弱一致性模型则快很多。
因此,放弃线性化是为了提高性能,而不是为了保住容错特性。