raft日志复制
why:
raft算法中,副本数据是以日志的形式产生的。即leader收到客户端的写请求后,处理写请求的过程就是:日志复制和提交的过程。
what:
日志格式:
日志是由日志项组成的,包括:用户指令(Command),索引值(log index),任期编码(term)。具体如下:
指令:一条由客户端指定、状态机需要执行的指令;也可理解为“客户端指定的数据”;
索引值:日志项在日志中对应的整数索引值。它是用来标记日志项在日志列表中位置的,是一个单调、递增的整数;
任期编号:创建日志项的leader所在的任期值;
how:
如何复制日志:
raft日志复制可以理解为优化后的二阶段提交(即一阶段提交替代了二阶段提交),那么就减少了1次rpc请求耗时。具体步骤如下:
a、领导者通过日志复制 RPC,将新的日志项复制到其他的服务器。
b、当领导者将日志项,成功复制到大多数的服务器上的时候,领导者会将这条日志项提交 到它的状态机中。
c、领导者将执行的结果返回给客户端。
d、当跟随者接收到心跳信息,或者新的日志复制 RPC 消息后,如果跟随者发现领导者已经 提交了某条日志项,而它还没提交,那么跟随者就将这条日志项提交到本地的状态机 中。
注意:领导者将日志项提交到它的状态,而没有通知跟随者提交?
原因:领导 者的日志复制 RPC 消息或心跳消息,包含了当前最大的,将会被提交的日志项索引值。所以跟随者就可以知道领导者的日志提交位置信息。
如何实现日志的一致性:
核心思想:领导者通过强制跟随者直接复制自己的日志项,处理不一致日志;
核心有 2 个步骤:
a、领导者通过日志复制 RPC 的一致性检查,找到跟随者节点上,与自己相同日志项的最大索引值。即:,这个索引值之前的日志,领导者和跟随者是一致的,之后的 日志是不一致的了。
b、领导者强制跟随者更新覆盖的不一致日志项,实现日志的一致。
注意:跟随者中的不一致日志项会被领导者的日志覆盖,而且领导者 从来不会覆盖或者删除自己的日志。
具体过程如下:
PrevLogEntry:表示当前要复制日志项的前面一条日志项的索引值。比如在图中,如 果领导者将索引值为 8 的日志项发送给跟随者,那么此时 PrevLogEntry 值为 7。
PrevLogTerm:表示当前要复制日志项的前面一条日志项的任期编号。比如在图中, 如果领导者将索引值为 8 的日志项发送给跟随者,那么此时 PrevLogTerm 值为 4。
a、领导者通过日志复制 RPC 消息,发送当前最新日志项到跟随者 ,例如:这个消息的 PrevLogEntry 值为 7,PrevLogTerm 值 为 4。
b、如果跟随者在它的日志中,找不到与 PrevLogEntry 值为 7、PrevLogTerm 值为 4 的日 志项。也就是说:它的日志和领导者的不一致了,那么跟随者就会拒绝接收新的日志项, 并返回失败信息给领导者。
c、这时,领导者会递减要复制的日志项的索引值,并发送新的日志项到跟随者。即:消息 的 PrevLogEntry 值为 6,PrevLogTerm 值为 3。
d、如果跟随者在它的日志中,找到了 PrevLogEntry 值为 6、PrevLogTerm 值为 3 的日志 项,那么日志复制 RPC 返回成功。则:领导者就知道在 PrevLogEntry 值为 6、 PrevLogTerm 值为 3 的位置,跟随者的日志项与自己相同。
e、领导者通过日志复制 RPC,复制并更新覆盖该索引值之后的日志项(也就是不一致的日 志项),最终实现了集群各节点日志的一致。