若有n个副本,且配置w和r,使得w + r > n w + r> nw+r>n,期望可以读到一个最新值。因为成功写入的节点集合和读取的节点集合必有重合,这样读取的节点中至少有一个具有最新值,如图-11。
一般设定r和w为简单多数(超过n/2)节点,即可确保 w + r> n,且同时容忍多达 n/2 个节点故障。但是,法定人数不一定必须是大多数,只是读写使用的节点交集至少需要包括一个节点。其他法定人数的配置是可能的,这使得分布式算法的设计有一定的灵活性。
您也可以将w和r设置为较小的数字,以使w + r ≤ n w + r≤nw+r≤n(即法定条件不满足)。在这种情况下,读取和写入操作仍将被发送到n个节点,但操作成功只需要少量的成功响应。
较小的w和r更有可能会读取过时的数据,因为您的读取更有可能不包含具有最新值的节点。另一方面,这种配置允许更低的延迟和更高的可用性:如果存在网络中断,并且许多副本变得无法访问,则可以继续处理读取和写入的机会更大。只有当可达副本的数量低于w或r时,数据库才分别变得不可用于写入或读取。
但是,即使在w + r > n w + r> nw+r>n的情况下,也可能存在返回陈旧值的边缘情况。这取决于实现,但可能的情况包括:
若使用宽松的法定人数,w个写入和r个读取落在完全不同的节点上,因此r节点和w之间不再保证有重叠节点【46】。
如果两个写入同时发生,不清楚哪一个先发生。在这种情况下,唯一安全的解决方案是合并并发写入(参阅处理写入冲突)。如果根据时间戳(最后写入胜利)挑选出一个胜者,则由于时钟偏差[35],写入可能会丢失
如果写操作与读操作同时发生,写操作可能仅反映在某些副本上。在这种情况下,不确定读取是返回旧值还是新值。
如果写操作在某些副本上成功,而在其他节点上失败(例如,因为某些节点上的磁盘已满),在小于w个副本上写入成功。所以整体判定写入失败,但整体写入失败并没有在写入成功的副本上回滚。这意味着如果一个写入虽然报告失败,后续的读取仍然可能会读取这次失败写入的值【47】。
如果携带新值的节点失败,需要读取其他带有旧值的副本。并且其数据从带有旧值的副本中恢复,则存储新值的副本数可能会低于w,从而打破法定人数条件。
即使一切工作正常,有时也会不幸地出现关于时序(timing) 的边缘情况
因此,尽管法定人数似乎保证读取返回最新的写入值,但在实践中并不那么简单。 Dynamo风格的数据库通常针对可以忍受最终一致性的用例进行优化。允许通过参数w和r来调整读取陈旧值的概率,但把它们当成绝对的保证是不明智的。
尤其是,因为通常没有得到“复制延迟问题”中讨论的保证(读己之写,单调读,一致前缀读),前面提到的异常可能会发生在应用程序中。更强有力的保证通常需要事务或共识。我们将在第七章和第九章回到这些话题。
4.2.1 监控旧值
运维角度,监视DB是否返回最新结果很重要。即使应用能容忍读旧值,也需了解复制的当前运行状况。若明显滞后,就是信号,需排查原因(如网络问题或节点超负荷)。
主从复制系统,DB通常会导出复制滞后的度量标准,可将其集成到监控系统。因为主、从节点的写都遵从相同顺序,而每个节点都维护了复制日志执行的当前偏移量。 通过对比主、从节点当前偏移值,即可衡量该从节点落后主节点程度。
无主复制系统,无固定写入顺序,因而监控也更难。且若数据库只使用读修复(无反熵过程),那么旧值的落后就无上限。例如若一个值很少被访问,则返回的旧值可能很老了!
衡量无主复制数据库的研究,根据参数n,w和r来预测旧值读取的预期百分比。不幸的是,这还不是常见做法,但将旧测量值包含在数据库的度量标准集中是好趋势。最终一致性是很模糊的保证,可操作性角度,能量化“最终”很有价值。