PACELC 基于 CAP 理论演进而来。
CAP 理论是一个分布式系统中老生常谈的理论了:
- C(Consistency):一致性,所有节点在同一时间的数据完全一致。
- A(Availability):可用性,服务一直可用。
- P(Partition tolerance):分区容错性,遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务
系统设计中,这三点只能取其二,一般的分布式系统要求必须有分区容错性。剩下的只能从 C 或者 A 中取舍。
但是这个理论并不能很好地应用于实际,首先, A 中是有一定争议的,很长时间才返回,虽然可用,但是业务上可能不能接受。并且,系统大部分时间下,分区都是平稳运行的,并不会出错,在这种情况下,系统设计要均衡的其实是延迟与数据一致性的问题,为了保证数据一致性,写入与读取的延迟就会增高。这就引出了 PACELC 理论。
在出现分区错误的情况下,取前半部分 PAC,理论和 CAP 内容一致。没有出现分区错误的情况下(PACELC 中的 E 代表 Else),取 LC,也就是 Latency(延迟)与 Consistency(一致性)。
现在,其实很多存储,都已经实现了不同的 PACELC 的兼顾策略,并且交由用户配置去灵活根据不同业务场景使用不同的策略。
DynamoDB,Riak,Cassandra 的 NWR 模型
例如 DynamoDB 和 Riak 还有 Cassandra 都是 Dynamo 理论论文的基于一致性哈希写多份实现最终一致性的存储,在默认情况下,是 P+A 以及 E+L 的系统,但是可以根据配置修改,主要基于NWR模型与同步和异步备份。N 代表 N 个备份,W 代表要写入至少 W 份才认为成功,R 表示至少读取 R 个备份。配置的时候要求 W+R > N。 因为 W+R > N, 所以 R > N-W。这个是什么意思呢?就是读取的份数一定要比总备份数减去确保写成功的倍数的差值要大。
也就是说,每次读取,都至少读取到一个最新的版本。从而不会读到一份旧数据。当我们需要高可写的环境的时候(例如,amazon 的购物车的添加请求应该是永远不被拒绝的)我们可以配置W = 1 如果N=3 那么R = 3。 这个时候只要写任何节点成功就认为成功,但是读的时候必须从所有的节点都读出数据。如果我们要求读的高效率,我们可以配置 W=N R=1。这个时候任何一个节点读成功就认为成功,但是写的时候必须写所有三个节点成功才认为成功。
大家注意,一个操作的耗时是几个并行操作中最慢一个的耗时。比如R=3的时候,实际上是向三个节点同时发了读请求,要三个节点都返回结果才能认为成功。假设某个节点的响应很慢,它就会严重拖累一个读操作的响应速度。
MongoDB
MongoDB 和上面的 Dynamo 类似,MongoDB关于一致性、可用性的权衡,取决于三者:
write-concern
: 表示当写请求在value个MongoDB实例处理之后才向客户端返回read-concern
: 设定是否必须从 primary 读取最新的数据还是可以从 secondary 读取最终一致性的数据。read-preference
: 对于replica set,是返回当前节点的最新数据,还是返回写入节点最多的数据,还是根据一些函数计算出的数据。
MySQL 同步
MySQL主从复制包括异步模式、半同步模式、全同步复制
默认情况下是异步模式,MySQL 一主多从部署读写分离的情况下,实现的为最终一致性,如果考虑一定延迟可以接受,一般可以通过 show slave status
来查看主从延迟从而决定数据是否可以从 slave 读取。 MyCat 等中间件就是用了这种机制。可以通过对于这个时延的容忍性,控制 L 与 C 的取舍 以及 A 与 C 的取舍。
全同步复制:指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。这样保证了强一致性,但是响应时间变长了。
半同步复制:主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到 relay log 中才返回给客户端。这样虽然还是有延迟,但是延迟小了很多并且数据相比于异步复制更加不容易丢失。
一致性协议
一致性协议一般包括:
- 2PC,两阶段提交
- 3PC,三阶段提交
- Paxos,Paxos 是很细致的一致性协议,但是一般实现过于复杂仅仅是理论
- Raft,Raft 是能够实现分布式系统强一致性的算法,TiDB 的一致性协议就是基于 Raft
- ZAB,Zookeeper 的一致性协议,基于 Paxos 简化
- NWR,上面提到的 dynamo 理论基础的协议,将 PACELC 均衡交给用户
每日一刷,轻松提升技术,斩获各种offer: