以上ReadIndex流程其实就是Follower Read的实现原理。注意MsgReadIndexResp返回的不仅仅是读请求本身,还有读请求到来时刻Leader的commitIndex,即那个时刻的最新的commitIndex。对于Follower来说,它也可以等待自己的appliedIndex == MsgReadIndexResp.commitIndex后执行队请求了。
你可能会疑惑,明明说的是Follower Read,为什么一条只读请求仍然经过了Leader且要check qurom?这样Leader岂不是仍然担负了读操作本身的开销?并不是这样的,这里Leader担负的仅仅是同步和通信的开销。读操作本身可能很大,例如说数据库扫描一整张表,这个操作本身并没有被Leader所执行,不过是这个请求在Leader这里走了一圈而已。当这个请求已经被Leader成功的做了Qurom Check之后,读表这个操作就可以在Follower这里做了。这也是为什么我前文中强调的,不要把读操作的理解狭义化,读操作本身不是在Raft算法里实现的,而是应用层实现的,Raft算法只是告知应用层可以做读请求了而已。
Lease Read需要依赖时间来保证强一致性(如果time drift过大就悲剧了)。Lease期的设定必须小于election_timeout,否则可能导致旧Leader的Lease期与新Leader的任期出现交叠,导致一种“双主”情景的出现。Lease在每次成功check_qurom会成功续约;在时间上,如果一个Leader在Lease期内,那么被其check_qurom的机器以及它本身会拒绝Lease期内发起preVote和RequeseVote的请求(具体原因请看前文中对拒绝投票原因的讨论),即Lease期内不会出现更新的Leader,因此读操作可以直接进行而无需为了这个读操作再进行一次check_qurom。
如果开启了Lease Read模式,那么ReadOnly请求就不会进行Qurom Check了,仅仅检查了一下自己是否是当前的Leader,就生成了ReadState并告知了应用层。根据 https://pingcap.com/blog-cn/tikv-source-code-reading-19/ 的描述,Lease Read的Lease期检查应该是被放在了应用层,因此Raft库中没有相关的代码。
Batch & Pipeline对于通信过程,可以通过Batch和Pipeline。注意TiKV的Raft库和通信的实现是解耦的,一切需要发送给其他peers的消息都会被放在RawNode.messages之中,由应用层取出并发送给对应的peer,因此Batch操作需要在应用层实现。具体来说就是将消息进行打包,减少元数据占据的总数据的比例以降低通信开销。但也必须要设定一个具体的范围,例如说等待多长时间后必须发送消息,或者对消息大小进行限制。
Leader要为每一个peer维护一个nextIndex,表明下一次给它们发送日志时要从nextIndex开始;Pipeline指的是无需等待上一轮的Msg得到相应的MsgResp,就可以发送新的Msg,即加快发送消息的效率。
Parallelly Append & Asynchronous Apply除了对通信的优化外,还有两个可以优化的地方:日志的持久化,以及日志的apply。
对于Leader来说,日志的持久化可以和AppendEntries并行进行。当然,在收到Qurom的确认前如果Leader尚未完成日志的持久化,那么它不能调整自己的commitIndex,因为调整commitIndex意味着日志本身已不可撤销。
日志的apply既包括执行也很可能涉及到新的落盘操作,开销不可忽视。实际上apply过程完全可以和Raft算法的执行并行进行,因为日志的apply改动的是状态机的状态(例如数据库就是一个状态机),而不是Raft算法所维护的状态。我们完全可以在日志成功commit之后,开启另一个线程去异步的Apply日志,通过回调来改动applyIndex;
参考文献[1] CONSENSUS: BRIDGING THEORY AND PRACTICE
[2] In Search of an Understandable Consensus Algorithm.
[3] https://pingcap.com/blog-cn/lease-read/
[4] https://youjiali1995.github.io/raft/etcd-raft-log-replication/
[5] https://pingcap.com/blog-cn/optimizing-raft-in-tikv/
[6] https://pingcap.com/blog-cn/tikv-source-code-reading-19/