分布式
分布式理论
CAP理论
CAP主要是在分布式项目下的一个理论。包含了三项,一致性、可用性、分区容错性
一致性(Consistency)是指更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致(强一致性),不能存在中间状态。
可用性(Availability) 是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
分区容错性(Partition tolerance) 是指分布式系统在遇到任何网络分区故障时,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。
🔴🟡🟢要么CP,要么AP:
首先一个前提,对于分布式系统而言,分区容错性是一个最基本的要求,因此基本上我们在设计分布式系统的时候只能从一致性(C)和可用性(A)之间进行取舍。
如果保证了一致性(C):对于节点N1和N2,当往N1里写数据时,N2上的操作必须被暂停,只有当N1同步数据到N2时才能对N2进行读写请求,在N2被暂停操作期间客户端提交的请求会收到失败或超时。显然,这与可用性是相悖的。
如果保证了可用性(A):那就不能暂停N2的读写操作,但同时N1在写数据的话,这就违背了一致性的要求。
BASE 理论
Basically Available(基本可用):基本可用是指分布式系统在出现不可预知的故障的时候,允许损失部分可用性,但不等于系统不可用。
Soft state(软状态):即是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
Eventually consistent(最终一致性):强调系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。其本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
BASE理论并不是CAP理论的延申
文章参考:https://juejin.cn/post/7345821800880422975
CAP理论的一致性探讨的是数据层面的一致性,而BASE理论的一致性探讨的是状态上的一致性
BASE与之对应的应该是ACID,他们的一致性才有的比较,ACID的一致性要求只有两种状态,比如付款只有未付款和付款了两种状态,而BASE允许出现中间的软状态,即可能事务未提交状态。
分布式缓存
分布式锁
Redis
优点:
- 简单易用: Redis 的分布式锁实现相对简单,容易上手。
- 性能高: Redis 是内存数据库,读写速度非常快,适合高并发场景。
- 支持扩展: 可以通过 Redis 的集群模式实现分布式锁的扩展。
缺点:
- 单点故障: 如果 Redis 是单点部署,一旦 Redis 宕机,整个分布式锁失效。
- 数据一致性: Redis 分布式锁的实现依赖于持久化机制,如果 Redis 实例故障导致数据丢失,可能会引起数据一致性问题。
- 不支持复杂锁特性: Redis 分布式锁通常只提供简单的锁特性,如阻塞、非阻塞等,不支持诸如共享锁、排他锁等复杂的锁特性。
适用场景:
- 简单场景: 对于简单的分布式锁需求,且系统已经使用 Redis 作为缓存或数据存储时,可以考虑使用 Redis 分布式锁。
- 高并发场景: Redis 的高性能适合处理高并发情况下的锁请求。
Zookeeper
优点:
- 高可用性: Zookeeper 是一个高可用的分布式协调服务,具有良好的容错机制,适合于构建分布式系统中的关键组件。
- 强一致性: Zookeeper 提供的是强一致性服务,能够保证分布式锁的数据一致性。
- 支持复杂锁特性: Zookeeper 可以实现更复杂的锁特性,如共享锁、排他锁等。
缺点:
- 复杂性: 相对于 Redis 分布式锁而言,Zookeeper 分布式锁的实现较为复杂,使用和部署都需要更多的技术和资源。
- 性能: 相比 Redis,Zookeeper 的性能较低,特别是在大规模锁竞争的情况下可能会出现性能瓶颈。
适用场景:
- 关键业务场景: 对于一些关键业务,需要高可靠性和数据一致性的场景,如分布式事务等。
- 复杂锁需求: 如果需要实现更复杂的锁特性,如共享锁、排他锁等,Zookeeper 更为适合。
- 已有 Zookeeper 部署: 如果系统已经使用 Zookeeper 作为分布式协调服务,可以考虑直接使用 Zookeeper 分布式锁。
强一致性
当说到 Zookeeper 提供强一致性服务时,这意味着它能够确保在分布式环境下,所有的客户端都能够看到相同的数据视图,并且在任何时刻,Zookeeper 集群中的所有节点都能够达成一致的数据状态。这种强一致性保证是通过 Zookeeper 的一致性协议(ZAB 协议)来实现的,它确保了以下几个方面的强一致性:
- 顺序一致性: Zookeeper 保证了所有更新操作的顺序一致性,即对于一个客户端而言,所有的更新操作都是按照它们发生的顺序来执行的。这意味着无论客户端连接到哪个 Zookeeper 节点,它们都会看到相同的更新顺序。
- 原子性: Zookeeper 保证了每个写操作都是原子的,要么成功完成,要么完全失败。这种原子性保证可以防止数据状态的不一致性。
- 单一系统镜像: Zookeeper 提供了单一系统镜像,即对于所有的客户端来说,它们都能够看到相同的数据视图,这消除了分布式系统中数据复制可能导致的不一致性。
锁类型
对于 Zookeeper 支持复杂锁特性的部分,它的锁服务(Zookeeper Recipes)可以实现多种锁类型,包括共享锁和排他锁等。这些锁的实现利用了 Zookeeper 的节点特性和监视机制:
- 排他锁(Exclusive Locks): 一次只允许一个客户端获取锁,其他客户端必须等待锁的释放。这种锁保证了在任何时刻只有一个客户端能够对资源进行操作,从而避免了并发访问带来的数据不一致性问题。
- 共享锁(Shared Locks): 允许多个客户端同时获取锁,这些客户端可以并发地对共享资源进行读操作,但是对于写操作仍然需要排他锁。共享锁提供了更高的并发性,适用于读多写少的场景。
通过 Zookeeper 的节点监听机制,客户端可以监听锁节点的状态变化,从而实现基于事件的锁释放机制,避免了常规轮询带来的性能损耗。
ZAB 协议
ZAB(ZooKeeper Atomic Broadcast)是 ZooKeeper 中实现一致性的协议。它是基于原子广播的分布式一致性协议,用于确保 ZooKeeper 集群中各个节点之间的数据一致性。
ZAB 协议的基本原理:
- Leader 选举: 在 ZooKeeper 集群中,一个节点会被选举为 Leader,负责协调客户端请求以及更新操作的提交。Leader 通过与其他节点保持心跳连接来维护其领导地位。如果 Leader 节点宕机或失去连接,ZooKeeper 集群会通过 ZAB 协议重新选举新的 Leader。
- 原子广播: 一旦 Leader 选举完成,Leader 将负责处理所有的写请求,并将这些写请求按照固定的顺序广播给所有的 Follower 节点。这个过程保证了所有的更新操作在 ZooKeeper 集群中被按照相同的顺序进行处理,从而确保了数据的一致性。
- 事务提议(Proposal): 当 Leader 收到客户端的写请求时,会将其封装成一个事务提议(Proposal)。Leader 将该提议发送给所有的 Follower 节点,要求它们在日志中记录该提议。
- 多数派(Majority): 在 ZAB 协议中,只有当大多数节点(包括 Leader)确认接收到了同一个提议后,该提议才被认为是已提交的。这种基于多数派的机制确保了数据的一致性,因为只有大多数节点都达成了相同的数据状态,才能保证整个系统的数据一致性。
- 持久化: 在将提议广播给 Follower 节点之前,Leader 必须将提议持久化到磁盘上,以保证即使在 Leader 宕机后,日志也不会丢失。
ZAB 协议的工作流程:
- Leader 选举: 当集群启动或者 Leader 节点失效时,ZooKeeper 集群会触发 Leader 选举过程,选举出新的 Leader 节点。
- 广播写请求: 一旦 Leader 选举完成,新的 Leader 将开始处理客户端的写请求,并将这些请求以提议的形式广播给所有的 Follower 节点。
- Follower 处理: Follower 节点接收到 Leader 的提议后,会将提议记录到自己的日志中,并发送确认消息给 Leader。
- 确认提交: 当大多数节点都确认接收到了同一个提议后,Leader 将该提议标记为已提交,然后再次广播给所有节点,确保所有节点都将该提议应用到自己的状态机中。
- 更新客户端: 一旦写操作被提交,Leader 将通知客户端写操作已经成功,客户端可以继续下一步操作。
通过 ZAB 协议,ZooKeeper 实现了分布式系统中数据的一致性和可靠性,确保了所有节点之间的数据状态是一致的。
https://www.cnblogs.com/crazymakercircle/p/14504520.html
分布式事务
https://www.cnblogs.com/crazymakercircle/p/13917517.html#autoid-h2-25-4-0
https://juejin.cn/post/6844904041659498509#heading-3
分布式ID
使用场景
除开分库分表外,通常还会用到分布式ID
的场景有:
- 链路
ID
:分布式链路中,需要通过全局唯一的traceId
来串联所有日志; - 请求
ID
:幂等性处理时,需要通过唯一的ID
来判断是否为重复请求; - 消息标识:
MQ
需要基于唯一的msgID
来区分数据,确保数据不重复或丢失; - 短链码:生成短链接时,需要获取一个全局唯一的值作为
Code
避免重复;
雪花算法
雪花算法(Snowflake Algorithm)是一种用来生成分布式系统中全局唯一的ID 的算法。由 Twiter 开发的,用于满足分布式系统中生成唯- ID 的需求。
雪花算法生成的唯一 ID 通常是一个 64 位的整数,按照以下结构组成:
- 首位符号位(固定为 0):符号位始终为 0,保证生成的是正整数。
- 41 位时间戳(亳秒级):表示生成 ID 的时间戳,可以支持约 69 年的时间范围。
- 10 位机器标识(分布式部署时的机器 ID): 可以支持 1024 台不同的机器。
- 12 位序列号(同一机器同一毫秒内的自增序列):表示同一台机器同一毫秒内生成的不同 ID 的序列号
雪花算法能生成分布式全局唯- ID 的原因:
- 雪花算法允许在同一毫秒内生成多个不同的 ID,通过序列号的自增保证在高并发情况下生成的 ID 唯一性。
- 通过机器部分的标识符保证了在不同的机器上生成 ID 时不会发生冲突。
- 利用时间戳部分的信息,确保生成的 ID 按时间递增,可以方便地对 ID 进行排序和分析。
UUID
UUID是一个长度为128位的标志符,能够在时间和空间上确保其唯一性。
UUID是基于时间戳、MAC地址、随机数等多种因素生成,理论上全球范围内几乎不可能重复。
优点:UUID不借助中心节点,可以保持程序的独立性,可以保证程序在不同的数据库之间,做数据迁移,都不受影响。
缺点:UUID生成的字符串太长,通过索引查询数据的效率比较低。此外,UUID生成的字符串,顺序没有保证,不是递增的,不满足工作中的有些业务场景。
数据库自增ID
优点:非常简单,数据查询效率非常高。
缺点:只能保证单表的数据唯一性,如果跨表或者跨数据库,ID可能会重复。ID是自增的,生成规则很容易被猜透,有安全风险。ID是基于数据库生成的,在高并发下,可能会有性能问题。
数据库号段模式
本质上还是数据库自增的ID,不过将一批一批的ID集合分段,比如有1000个连续自增的ID,都将其存入缓存中,分布式取ID时先从缓存中取,找不到了再从数据库中继续分段存入缓存,再从缓存中取ID
优点:实现简单,对数据库的依赖减弱了,可以提升系统的性能。
缺点:ID是自增的,生成规则很容易被猜透,有安全风险。单节点宕机问题
数据库的多主模式
本质上是上面的号段模式为了解决单个数据库节点宕机,而采用多个master数据库节点来共同实现号段模式。首先各节点约定好各自生成ID的策略,为的就是多节点之间生成的ID不重复,是唯一的,比如1节点生成的ID都是11开头,2节点生成的ID都是12开头。
优点:避免了数据库号段模式的单节点yan风险,提升了系统的稳定性,由于结合使用了号段模式,系统性能也是OK的。
缺点:跨多个master实例下生成的ID,可能不是递增的。
分布式链路追踪
黑盒
唯一的traceId去索引
分布式限流
为什么选择分布式限流?
高并发场景下的稳定性:
- 在高并发场景下,单个服务器节点很容易达到处理极限,分布式限流可以在多个节点之间分散请求负载,防止任何一个节点过载。
全局一致性:
- 单机限流无法保证全局的一致性,尤其是在多个服务器节点上部署的情况下。分布式限流确保在整个系统范围内限制流量,避免局部过载导致的问题。
可扩展性:
- 随着业务的增长,系统需要水平扩展。分布式限流的设计支持横向扩展,使得随着节点数量增加而自动调整限流策略。
灵活性:
- 分布式限流可以根据业务需求动态调整限流规则,比如在高峰期增加限流阈值,低峰期减少限流阈值。
如何实现?
技术选型:
- 使用了
Redisson
作为分布式锁和限流工具,它提供了一套完整的分布式协调服务,易于集成和使用。 - 使用
Redis
作为共享存储,用于存储限流计数器等信息,确保数据的全局可见性。
- 使用了
实现细节:
- 实现了一个基于令牌桶算法的分布式限流器,该算法可以平滑突发流量,确保系统的稳定运行。
- 利用
Redisson
提供的RateLimiter
接口来实现限流逻辑。 - 在每次请求到达时,首先尝试获取令牌,如果获取成功则继续处理请求;如果获取失败,则根据业务需求选择丢弃请求或者排队等待。
异常处理:
- 实现了重试机制,当获取令牌失败时,可以根据业务需求设置重试次数和间隔时间。
- 当
Redis
服务不可用时,系统需要有备份计划或者降级策略,确保服务的可用性。
为什么不这么做还有别的解决方案?
单机限流:
- 在小型系统或低并发场景下,可以使用简单的单机限流机制,如基于内存的限流。
- 使用
Guava RateLimiter
等工具可以方便地实现单机限流。
混合限流:
- 结合单机限流和分布式限流的优点,对于高频热点资源采用分布式限流,而对于普通资源可以使用单机限流。
- 这种混合方案可以减少分布式组件的依赖,同时保证系统的稳定性和扩展性。
第三方服务:
- 使用云服务商提供的限流服务,如阿里云的API网关、腾讯云的API网关等,它们通常提供了成熟的限流功能。
- 这种方式可以减少自己实现分布式限流的复杂度,但可能会增加额外的成本。
具体流程
假设调用了一次AI推荐请求,以下是具体的流程:
- 请求到达:
- 用户发起一次AI推荐请求,请求到达其中一个服务器节点。
- 限流检查:
- 服务器节点首先查询中心化存储(Redis)中的限流状态。
- 检查当前时间窗口内是否还有足够的令牌(或配额)可以使用。
- 处理请求:
- 如果有足够的令牌:
- 从令牌桶中消耗一个令牌。
- 继续处理AI推荐请求。
- 如果没有足够的令牌:
- 根据业务需求,可以选择拒绝请求或将其加入等待队列。
- 如果有足够的令牌:
- 更新限流状态:
- 处理完请求后,更新中心化存储中的限流状态。
- 如果使用的是令牌桶算法,随着时间推移会自动添加令牌。
- 返回结果:
- 请求处理完成后,返回结果给用户。
- 异常处理:
- 如果Redis不可用,可以使用本地缓存或其他备份机制处理请求。
- 如果Redis恢复正常,重新启用分布式限流。
🔴🟡🟢总结:通过这样的流程,分布式限流能够在高并发场景下有效地控制每个节点的请求负载,确保整个系统的稳定性和响应时间。
分布式体现在哪里
- 跨节点一致性:
- 分布式限流确保在多个服务器节点之间共享限流状态,这意味着无论用户请求哪个节点,都遵循相同的限流规则。
- 这是通过使用中心化的存储系统(如Redis)来实现的,所有节点都可以访问相同的限流状态。
- 全局一致性:
- 分布式限流确保在整个系统范围内限制总的请求流量,而不是仅限于单个节点。
- 例如,如果系统有三个节点,每个节点每秒可以处理100个请求,那么在分布式限流的场景下,整个系统每秒只能处理300个请求,而不是每个节点独立处理100个请求。
- 可扩展性:
- 分布式限流的设计支持系统的水平扩展,即随着节点数量的增加,可以通过调整限流策略来自动适应。
- 当系统需要扩展时,只需要增加节点数量,而不需要更改限流逻辑。
- 容错机制:
- 分布式限流通常包含容错机制,比如当中心化存储系统(如Redis)不可用时,可以使用本地缓存或其他备份机制来处理请求,以确保服务的可用性。
接口幂等性
- 数据库唯一索引
- 先select后insert
- 悲观锁
- 乐观锁
- 唯一token保存在redis里去每次校验
- 使用状态机,类似下单操作的各个状态往下推进
- 采用分布式锁