读取到旧数据(异步提交的数据不一致)
那各个节点数据在任意时间点读出来都是一致的吗?什么情况下会读到旧数据呢?
1)Client 发起一个更新 hello 为 world 请求;
2)Leader 收到写请求,它会将此请求持久化到 WAL 日志,并广播给各个节点(并发的,一方面广播给 Follower 节点,一方面自己写 WAL);
若一半以上节点持久化成功,则该请求对应的日志条目被标识为已提交,etcdserver 模块异步从 Raft 模块获取已提交的日志条目,应用到状态机(如 boltdb 等);
Client 发起一个读取 hello 的请求,假设此请求直接从状态机中读取, 如果连接到的是 C 节点,若 C 节点磁盘 I/O 出现波动,可能导致它应用已提交的日志条目很慢,则会出现更新 hello 为 world 的写命令,在 client 读 hello 的时候还未被提交到状态机,因此就可能读取到旧数据;
ReadIndex
前面我们聊到串行读时提到,它之所以能读到旧数据,主要原因是 Follower 节点收到 Leader 节点同步的写请求后,应用日志条目到状态机是个异步过程,那如何保证读取到最新的数据呢?
其实这个机制就是叫 ReadIndex,它是在 etcd 3.1 中引入的。该机制保证:在读取的时候,确保最新的数据已写入状态机中;
原理如图:
线性读通过 ReadIndex 机制保证数据一致性原理,大致原理如下:
当然还有其它机制也能实现线性读,例如,在早期 etcd 3.0 中读请求通过走一遍 Raft 协议保证一致性,这种 Raft log read 机制依赖磁盘 IO,性能相比 ReadIndex 较差;