MySQL的after_sync半同步与raft 保证一致性的方式有些类似。
after_sync是master在sync binlog后等待从库把事务写到relay log后的ack,拿到ack后,在commit,然后在返回给客户端提交成功的信息。
raft中的日志有commit和applied 两个列表,commited 代表日志写入了文件,applied代表日志应用到了状态机。
raft也是leader先写日志,然后在等待大部分的节点信息都写入了日志,提交了,然后leader在提交,提交日志后,leader在应用到状态机,然后在返回给客户端提交成功的信息, 给其他节点提交信息,其他节点应用日志到状态机,其他节点网络慢的情况下,leader会不停重试传输。
针对leader1宕机的几种状态下的故障。参考
https://www.cnblogs.com/mindwind/p/5231986.html
https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md
https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-06-raft1/6.6-ying-yong-ceng-jie-kou
针对上面的场景有几个疑问
一:场景3中,新主会间接提交之前的日志,客户端在重试,不是重复执行了吗?状态机执行了2次命令。
这个状态机不会重复执行,因为之前的leader已经挂了,还没有apply log,所以不会发送apply 的请求出去。
二:如果leader apply log,返回给客户端确认,但是follower没有收到apply的信号,leader就挂了,虽然新主上有commit的日志,但是不会apply,怎么办?
这个是会应用的,新的leader,在接受用户请求之前会执行
r.dispatchLogs([]*logFuture{noop})
分发一个noop日志
func (r *Raft) processLogs(index uint64, future *logFuture) {
// Reject logs we've applied already
lastApplied := r.getLastApplied()
if index <= lastApplied {
r.logger.Printf("[WARN] raft: Skipping application of old log: %d", index)
return
}
// Apply all t