“一致性”是镜花水月

分布式系统要达到强一致性(一致性), 所有的实现算法最终都是基于 W+R > N
, 例如将 W 和 R 设置成多数派(后面按这个设置讨论), 即 N/2+1
. 具体是指:

1. 尝试写全部节点, 如果写了 W 个节点就算是写成功.
2. 同样, 读的时候读全部节点, 如果 R 个节点达到一致(注意, 不是读多数派, 而是多数派达成共识).
3. 最后, 也是最重要的, 如果无法满足前面两个条件, 必须强制将系统设置为”不可用”状态.
最后一项条件非常重要, 如果不强制将系统设置为不可用, 将违反一致性原则.
工程实践上极度厌恶”不可用”, 追求的是”高可用”, 所以, 最后一项很少有人能完全坚持. 这也是各种分布式系统优化的基础. 大部分的工程都是对读操作的优化, 不要求读操作达到多数派, 从而避免去执行第 3 项.
例如, 系统可以只读取唯一个节点, 返回该节点的数据即可. 这样做有工程上的价值. 如果某个节点故障, 换另一个节点继续尝试(例如一致性 hash).
更进一步(更接近强一致性, 但仍然不是)的做法, 可以读取多数节点, 但不要求多数节点达成共识, 只返回多数节点之中最新版本的数据.
这些对于读操作的优化, 都只能达到数据本身的”最终一致性”, 从用户的观察角度看, 系统的状态在某个时间段是飘忽不定的(不一致). 无论你是否愿意承认, 只要你违反了 3 项原则中的任何一项, 你的系统就不是强一致性的. 不管你借鉴了 Paxos 还是 Raft. Raft 比较特殊一点, 把 Leader 当作存储共识的单点, 只需要查询 Leader 就能知道是否达成共识, 写或者读.
强一致性既然那么难, 所以工程不太可能会去实现强一致性的系统. 正如前面所说, 大部分都对读操作进行了简化(优化). 那么, 写操作还是强一致的吗? 答案是否定的. 一旦读不是强一致性的, 写便不再是强一致性的. 一致性是对整个系统而言.
那么, 很多借鉴了 Paxos 或者 Raft 的写流程的系统, 它们在做什么? 其实, 大部分只是利用了多数派顺序写特性. 多数派(多副本)是为了达到高可靠性, 顺序写是为了最终一致. 例如 Paxos 的 prepare 阶段就像是在申请全局递增的序号, 而 accept 阶段是拷贝多副本.
是的, 大部分系统最终实现的是高可靠, 高可用, 和最终一致性.