分布式理论: 再谈CAP
Table of Contents
分布式理论: 再谈CAP
背景介绍
CAP1理论出现已经几十年了, 一个理论是有保质期的, 随着时代的进步, 理论需要不带的更新迭代.
传统的 CAP 理论认为, CAP 三者是无法并存的, 即 强一致性 & 100% 可用性 & 分区容忍性 最多是只能保证其中两个, 这是有着严格的数学证明的, 但是在实际工程的应用上, 情况有了很大的变化
本文探究在现代分布式网络架构下, 传统的 CAP 理论所面临的挑战以及一些解决方案.
什么是 CAP?
一致性( C )
CAP 理论中的一致性是指强一致性( Strong Consistency ),又叫线性一致性( Linearizable Consistency ),它要求多节点组成的分布式系统,能像单节点一样运作,如果一个写操作返回成功,那么之后的读请求都必须读到这个新数据;如果返回失败,那么所有的读操作都不能读到这个数据。
换言之, 当一个写操作得到确认后, 后续的任意读操作, 必定可以读到最新的数据.
可用性( A )
CAP 理论对可用性的定义,指的是要求系统提供的服务必须处于 100% 可用的状态,对于用户的每一个操作请求,系统总能够在有限的时间内返回结果。三个关键点:“ 100% 可用”、“有限时间内”和“返回结果”。
分区容错性( P )
分区指的是在整个分布式系统中,因为各种网络原因,系统被分隔成多个单独的部分,它不仅包含我们通常说的网络分区,也包含因为网络丢包导致的网络不通的情况。
现实的网络环境中, 始终存在的两个问题
- 不可靠的网络
- 概率性的宕机
因此分布式系统中, 分区的出现是不不可避免的, 所以对于绝大多数的分布式系统来说, 分区容忍性是必须保证的.
现实问题
100% 可用性是伪命题
在网络环境下, 哪怕分布式系统本身保证了 100% 的可用性, 但是由于调用方大概率是通过网络进行请求, 这导致网络请求的可用性是会直接影响到外部观测到的实际可用性.
举例, 我们假设以一个 Deployment 的形式部署了一个 etcd 服务, 对外以 service 的形式进行暴露, 那么此时的连通性就依赖 service 的可用性
因此, 哪怕系统本身实现了 100% 的可用性, 依然无法保证外部观测的实际可用性.
所以在实际的生产环境中, 100% 的实际可用性是一个伪命题, 即服务本身应当追求高可用, 但是无法真正达成 100% 可用.
再谈可用性
一个服务必须要保证 100% 的可用性吗?
答案是不需要, 也做不到, 只要能够保证高可用, 例如 99.999% 的可用性, 在实际的调用测可以通过 重试, 重连, 降级等等策略, 来保证在服务不可用时, 提供一个符合需求的可用能力
一致性降级
通常来讲一致性有以下几种分级
- 强一致性:在分布式系统中,如果一个写操作完成,那么所有的读操作都能够立即看到这个写操作的结果。这种一致性级别要求非常高,通常需要牺牲一定的性能来保证数据的强一致性。
- 弱一致性:在分布式系统中,数据的一致性不是即时的,可能会有一段时间的延迟。在这段时间内,系统可能处于不一致的状态。弱一致性通常用于一些对实时性要求不高的场景。
- 最终一致性:在分布式系统中,数据的一致性是最终达成的,即系统保证在一段时间内会达到一致的状态。最终一致性是一种折中的方式,可以在一定程度上保证数据的一致性,同时提高系统的性能。
- 事件 ual 一致性:事件 ual 一致性是最弱的一致性级别,系统在出现分区或故障时可以容忍数据的不一致性,但最终会达到一致的状态。
在 AP 系统中, 我们几乎无法达成强一致性, 但是我们可以追求更低等级的一致性, 因此哪怕在 AP 系统中, 依然存在着一致性的保证.
AP 再延伸: BASE
基本可用(Basically Available)
再谈可用性
软状态(Soft state)
因为不会对外部暴露出, 暂时不处理
最终一致性(Eventually consistent)
由于典型的分布式系统中, 总会存在着主备/ 读写分离的情况, 因此强一致性的保证会更加的困难
因此如果能够保证, 在一定的时间内, 能够在不同的节点上保证数据是一致的, 在绝大多数的情况下, 着就满足了我们的需求.
主流分布式系统的能力思考
Redis Cluster
Redis Cluster 通过数据 sharding, 以及主备机制来提供良好的可用性
有几个关键的技术
-
哈希槽: Redis Cluster 不使用一致性哈希,而是选择了一个固定数量(16384 个)的哈希槽。每个键通过 CRC16 算法对键名进行哈希计算,然后取模 16384 来决定应该放入哪个槽。 每个 Redis Cluster 节点(实例)负责维护一部分哈希槽,例如一个包含 3 个节点的集群可能会有一个节点负责 0-5460 号槽,另一个节点负责 5461-10922 号槽,第三个节点负责 10923-16383 号槽。 当需要在集群中寻找一个键的位置时,Redis 客户端会计算该键的哈希槽,并与集群中的节点信息对照,找到负责该槽的节点,然后直接与该节点通信。
-
gossip Gossip 协议是一种节点间通信机制,它允许集群中的节点通过一个不断传播的过程来交换信息。这种方式类似于社交网络中的八卦传播,因此得名 Gossip。 在 Redis Cluster 中,各个节点周期性地向其他随机选择的节点发送信息,这些信息包括但不限于:
- 节点的状态(例如,是否可达、是否主节点、是否投票等)
- 集群的拓扑信息
- 节点的配置变化
- 键空间的变化(例如,哪些槽(slot)被指派给了哪个节点) Gossip 协议的优点在于它的扩展性和鲁棒性。因为信息是通过节点间的小量随机交换来传播的,即使有部分节点失效或网络出现问题,集群的其余部分仍然可以继续交换信息,并最终达到一致的状态视图。
MongoDB sharding
MongoDB 将数据分布在多个服务器上,称为分片(shards)。每个分片都是一个独立的数据库,集合中的数据分布在所有分片上。这种方法可以提高应用程序的可伸缩性和性能,因为它允许数据库跨多个机器进行操作,从而充分利用分布式计算资源。
MongoDB 的分片环境通常包括以下几个组件:
- 分片(Shards) :每个分片都是一个物理的数据库服务器,负责存储数据的子集。分片可以是一个单独的 MongoDB 实例,也可以是一个复制集。
- 查询路由器(Query Router) :查询路由器是由
mongos 进程实现的,它接收客户端应用程序的请求,并将这些请求路由到适当的分片。客户端与mongos进程交互,就像它是单个数据库服务器一样。 - 配置服务器(Config Servers) :配置服务器存储整个分片环境的元数据和配置信息。这包括数据在分片之间如何分布的信息,以及集群的状态。通常有三个配置服务器,以确保高可用性和数据一致性。
在 MongoDB 中设置分片涉及以下步骤:
- 选择分片键(Shard Key) :分片键是集合中的一个字段,用于决定数据如何在分片之间分布。选择一个好的分片键至关重要,因为它会直接影响到集群的性能和平衡。
- 配置分片环境:设置分片环境包括启动
mongod 实例作为分片,配置mongos查询路由器,以及初始化配置服务器。 - 启用分片:在一个集合上启用分片,将其变成一个分片集合。这涉及到在
mongos上执行命令,来指定分片键并启用分片。 - 数据均衡(Balancing) :MongoDB 会自动进行数据均衡,将数据块(chunks)从一个分片移动到另一个分片,以保持数据在分片之间的均匀分布。
etcd
需要注意的是, 哪怕处于不可用状态时, 依然提供读的能力.
Raft 协议
-
Leader Election(领导者选举) :
- 系统启动时,节点开始作为 Follower。
- 如果 Follower 在一定时间内没有收到 Leader 的消息,它会成为 Candidate 并开始选举过程。
- Candidate 请求其他节点的投票。
- 收到大多数节点投票的 Candidate 成为 Leader。
-
Log Replication(日志复制) :
- Leader 接收客户端的请求,将请求作为新的日志条目追加到它的日志中。
- 然后 Leader 将这个日志条目复制到集群中的其他 Follower 节点。
- 一旦这个日志条目被安全地复制(即复制到大多数节点),它将被提交并应用到状态机。
-
Safety(安全性) :
- Raft 保证了即使在出现网络分区、节点故障等问题时,已经提交的日志条目也不会丢失,并且集群最终都将达到一致的状态。
如何保证 CAP
- Consistency:
etcd使用 Raft 协议来保证一致性。所有的写操作都通过 Leader 节点来协调,确保每个操作都被复制到集群的大多数节点上,并且以相同的顺序应用。 - Availability: ** **
**在没有网络分区的情况下,**
etcd可以提供较高的可用性。但是,当发生网络分区时,只有那些包含大多数节点的分区可以继续提供服务,以保证数据的一致性。 - Partition Tolerance: ** **
etcd通过 Raft 协议的日志复制机制来处理网络分区。即使在分区期间,系统也能保持一致的状态,等待网络恢复后同步状态。
参考
ReidsCluster: https://blog.csdn.net/weixin_**40242845/article/details/127089280**