关于线性一致性读的定义,简单而言就是在 T 时刻执行写入操作,那么在 T 时刻之后一定能够读取到之前写入的值。Raft 算法能够至少保证集群节点数据的最终一致性,也就说就某一特定时刻而言各个节点之间的数据状态允许存在滞后。在分布式场景下如果允许各个节点随机响应用户的读请求,则可能会读取到脏数据。如果希望基于 Raft 算法实现线性一致性读语义,最简单的方式就是将读操作作为一个指令提交给 Raft 集群,由于 Raft 算法能够保证指令执行的顺序性,所以该读操作指令一定能够读取到在此之前写入的值(本文将此类线性一致性读策略命名为 RaftLog Read)。然而,RaftLog Read 的缺点也是显而易见的,每次读操作都需要走一遍完整的 Raft 算法流程势必效率低下,并且大部分的应用场景都具备读多写少的特征,所以该策略势必会让 Raft 集群产生大量的日志文件,增加磁盘和网络的开销。
换一个角度思考,在 Raft 算法中更新操作都由 Leader 节点负责响应,那么极端一点每次都从 Leader 节点读数据是不是就万事大吉了呢?先不说这种方式将一个分布式系统退化成了单机系统,我们还需要考虑下面两个问题:
- 日志数据从提交到被业务状态机所应用这中间存在一定的时间滞后性,所以直接执行读操作不一定能够读取到最新的数据。
- 当前 Leader 节点不一定是有效的,因为 Leader 节点的变更通常有一个时间差,而这中间存在导致脏读的可能性。