Good good study, day day up

aleung的学习笔记, aleung的idea

CAP

更新一致性

更新一致性问题发生在多个实体同时更新同一数据,出现写冲突。并发环境下维护数据一致性所用的方式通常分为悲观和乐观两种。

悲观方式就是避免发生冲突,例如采用 write lock,根本不让写操作同时发生。排在后面的操作者需要检查最新数据来判断是否继续更新。

乐观方式是先让冲突发生,然后检测冲突并处理。例如用条件更新,仅当数据库中的数据当前值与之前读出的值相同时才对此执行更新操作。另一种方法是记录不同版本的数据,当不同版本数据发生冲突时需要采取某种策略去合并(merge)数据。

在存在数据复制的分布式系统中,需要保证不同节点的更新操作顺序是保持一致的,否则无法解决数据一致性。但分布式系统中顺序一致性往往难以保障,常用的方法是将某份数据的所有写入操作都交由一个节点来完成(数据分片机制)。

读取一致性(读写冲突)

由于存放于不同聚合的相关数据分成多个步骤更新,在更新过程中读取数据可能产生违反“逻辑一致性”的情况。关系数据库通过事务来解决此问题。面向聚合的NoSQL 数据库通常只能在单一聚合内部保证更新操作的原子性,无法保证跨聚合更新的原子性。存在不一致风险的时间长度称为“不一致窗口”。

当数据需要复制副本时,从节点获取到更新后的数据需要一定的时间,在未成功复制之前,数据存在违反“复制一致性”的问题。最终更新操作还是会传播到所有节点,从而得到更新后的数据,这种情况叫做“最终一致性”,也就是说,在任意时刻,节点中都可能存在复制不一致问题,但陈旧数据最终都能得到更新。

视乎应用逻辑可以忍受多长的不一致窗口,可以采取不同策略。例如保证“照原样读出所写内容的一致性”(read-your-writes consistency),这可以通过提供 session 一致性来保障,例如采用 sticky session,或者检查数据的 version stamp 。

CAP

CAP定理是需要放宽一致性约束的原因。CAP 包括:一致性 Consistency,可用性 Availability,分区耐受性 Partition tolerance。三者不可能同时满足。

CAP 三个概念不能从字面上想当然去理解。

简单的说,一致性保证要求往分布式系统的操作的效果看起来就像一个节点上操作一样,在同一个逻辑瞬间中在所有节点上完成。

可用性是说系统中某个无故障节点对所收到的每一请求必须给予确定结果响应。在考虑到P 的情况下,即使出现脑裂,节点依然要能够给出响应。通俗的例子:当你还活着,我找到你,你就必须对我的请求给出明确答复,不能说因为别的节点联系不上了所以给不了答复。需要注意定义中的无故障节点前提,CAP 考虑的是此前提之下的群集节点可用性。

分区耐受性是指分布式系统需要能允许节点之间出现通信故障,导致部分消息丢失,甚至集群被分割为多个无法相互通信的分区(脑裂)。

CA之间的取舍

在实践中,P 是必须保障的,因为不存在绝对不会发生故障的网络。那么问题就是在 C 和 A 之间如何平衡。

在出现分区时,如果要优先考虑 C 而放弃 A,那么可以完全停止服务,拒绝写操作(如二阶段提交),或者仅仅对数据的 master node 在本分区的请求提供服务。

如果优先考虑 A 而放弃 C,那么节点可以继续响应任何请求,即使返回的可能是陈旧数据,或者可能出现写数据冲突。数据冲突在通信恢复后可以通过版本戳侦测出来,然后通过某种(应用指定的)策略去合并。

实际上,系统设计不会追求完美的一致性或者完美的可用性,而是根据需求在其中折中。例如允许更长的不一致窗口,降低一致性要求来换取可用性提升。

仲裁 Quorum 可以在发生分区问题时保障一致性,视乎分区问题的严重性。当复制因子为 N(数据副本数量),成功写入节点数为 W,成功读取节点数为 R,当 R+W>N 时可满足一致性要求。

另一方面,一致性也与 latency 相互排斥,当引入更多手段来保证一致性时(如仲裁),latency 会增加,或者说当允许更大的 latency 时,可以增加一致性保障(例如不断重试)。

放宽持久性约束

某些场合可以牺牲一些持久性以换取更好性能,也就是要忍受在故障情况下部分数据丢失。

Reference