1.强一致性与最终一致性
1.1强一致性
强一致性能保证写操作完成后,任何后续访问都能读到更新后的值;强一致性可以保证从库有与主库一致的数据。如果主库突然宕机,我们仍可以保证数据完整。但如果从库宕机或网络阻塞,主库就无法完成写入操作。
1.2最终一致性
最终一致性只能保证如果对某个对象没有新的写操作了,最终所有后续访问都能读到相同的最近更新的值。开篇提到,容忍节点故障只是需要复制的一个原因。另两个原因是可扩展性和降低延迟。
单领导者的主从复制算法要求所有写入都由单个节点处理,但只读查询可以由任何节点处理。对于读多写少的场景,我们往往创建很多从库,并将读请求分散到所有的从库上去。这样能减小主库的负载,并允许向最近的节点发送读请求。当然这只适用于异步复制——如果尝试同步复制,则单个节点故障将使整个系统无法写入。
当用户从异步从库读取时,如果此异步从库落后,他可能会看到过时的信息。这种不一致只是一个暂时的状态——如果等待一段时间,从库最终会赶上并与主库保持一致。这称为最终一致性。
最终两个字用得很微妙,因为从写入主库到反映至从库之间的延迟,可能仅仅是几分之一秒,也可能是几个小时。
2.Quorum NWR 的三要素
2.1副本数
N 表示副本数,又叫做复制因子(Replication Factor)。也就是说,N 表示集群中同一份数据有多少个副本,就像下图的样子:
从图中你可以看到,在这个三节点的集群中,DATA-1 有 2 个副本,DATA-2 有 3 个副本,DATA-3 有 1 个副本。也就是说,副本数可以不等于节点数,不同的数据可以有不同的副本数。
在实现 Quorum NWR 的时候,你需要实现自定义副本的功能。也就是说,用户可以自定义指定数据的副本数,比如,用户可以指定 DATA-1 具有 2 个副本,DATA-2 具有 3 个副本,就像图中的样子。
2.2写一致性级别
W,又称写一致性级别(Write Consistency Level),表示成功完成 W 个副本更新,才完成写操作:
从图中你可以看到,DATA-2 的写副本数为 2,也就说,对 DATA-2 执行写操作时,完成了 2 个副本的更新(比如节点 A、C),才完成写操作。
那么有的人会问了,DATA-2 有 3 个数据副本,完成了 2 副本的更新,就完成了写操作,那么如何实现强一致性呢?如果读到了第三个数据副本(比如节点 B),不就可能无法读到更新后的值了吗?先继续看下面的内容。
3.读一致性级别
R,又称读一致性级别(Read Consistency Level),表示读取一个数据对象时需要读 R个副本。你可以这么理解,读取指定数据时,要读 R 副本,然后返回 R 个副本中最新的那份数据:
从图中你可以看到,DATA-2 的读副本数为 2。也就是说,客户端读取 DATA-2 的数据时,需要读取 2 个副本中的数据,然后返回最新的那份数据。
这里需要你注意的是,无论客户端如何执行读操作,哪怕它访问的是写操作未强制更新副本数据的节点(比如节点 B),但因为 W(2) + R(2) > N(3),也就是说,访问节点 B,执行读操作时,因为要读 2 份数据副本,所以除了节点 B 上的 DATA-2,还会读取节点 A 或节点 C 上的 DATA-2,就像上图的样子(比如节点 C 上的 DATA-2),而节点 A 和节点 C的 DATA-2 数据副本是强制更新成功的。这个时候,返回给客户端肯定是最新的那份数据。
你看,通过设置 R 为 2,即使读到前面问题中的第三份副本数据(比如节点 B),也能返回更新后的那份数据,实现强一致性了。
4.NWR组合
除此之外,关于 NWR 需要你注意的是,N、W、R 值的不同组合,会产生不同的一致性效果,具体来说,有这么两种效果:
当 W + R > N 的时候,对于客户端来讲,整个系统能保证强一致性,一定能返回更新后的那份数据。
当 W + R < N 的时候,对于客户端来讲,整个系统只能保证最终一致性,可能会返回旧数据。