分布式理论

CAP 原则

在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性), 三者不可得兼

  • 一致性(C) :  在分布式系统中的所有数据备份, 在同一时刻是否同样的值(等同于所有节点访问同一份最新的数据副本)
  • 可用性(A):  在集群中一部分节点故障后, 集群整体是否还能响应客户端的读写请求(对数据更新具备高可用性)
  • 分区容忍性(P):  以实际效果而言, 分区相当于对通信的时限要求. 系统如果不能在时限内达成数据一致性, 就意味着发生了分区的情况, 必须就当前操作在 C 和 A 之间做出选择

分布式锁

分布式锁是用于分布式环境下并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用

Redis 实现

使用 Redis 中的 SET NX,实现只有 Key 不存在才插入

  • 如果 key 不存在,则显示插入成功,可以用来表示加锁成功;
  • 如果 key 存在,则会显示插入失败,可以用来表示加锁失败。

加锁

SET lock_key unique_value NX PX 10000
  • lock_key 就是 key 键;
  • unique_value 是客户端生成的唯一的标识,区分来自不同客户端的锁操作;
  • NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
  • PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。

原子性保证

加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作,但需要以原子操作的方式完成,使用 SET 命令带上 NX 选项来实现加锁

过期设置

锁变量需要设置过期时间,以免客户端拿到锁后发生异常,导致锁一直无法释放,在 SET 命令执行时加上 EX/PX 选项,设置其过期时间

客户端区分

锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作,使用 SET 命令设置锁变量值时,每个客户端设置的值是一个唯一值,用于标识客户端

解锁

解锁就是将 lock_key 键删除

// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
  return redis.call("del",KEYS[1])
else
  return 0
end

客户端区分

要保证执行操作的客户端就是加锁的客户端。所以解锁的时候要先判断锁的 unique_value 是否为加锁客户端,是的话,才将 lock_key 键删除

原子性保证

解锁有两个操作,需要 Lua 脚本来保证解锁的原子性

Zookeeper 实现

分布式事务

解决方案

方案 一致性 性能 复杂度 适用场景
2PC 强一致性 传统数据库、XA 协议
3PC 强一致性 中低 需减少阻塞的强一致场景
TCC 最终一致性 高并发业务(支付、库存)
Saga 最终一致性 长事务、跨服务流程
消息队列 最终一致性 事件驱动架构
本地消息表 最终一致性 异步通知(订单-积分)
  • 两阶段提交协议(2PC):为准备阶段和提交阶段
    • 准备阶段,协调者向参与者发送准备请求,参与者执行事务操作并反馈结果。
    • 若所有参与者准备就绪,协调者在提交阶段发送提交请求,参与者执行提交;否则发送回滚请求。
    • 实现简单,能保证事务强一致性。
    • 存在单点故障,协调者故障会影响事务流程;性能低,多次消息交互增加延迟;资源锁导致资源长时间占用,降低并发性能。
    • 适用于对数据一致性要求高、并发度低的场景,如金融系统转账业务。
  • 三阶段提交协议(3PC):在 2PC 基础上,将准备阶段拆分为询问阶段和准备阶段,形成询问、准备和提交三个阶段。
    • 询问阶段协调者询问参与者能否执行事务,后续阶段与 2PC 类似。
    • 降低参与者阻塞时间,提高并发性能,引入超时机制一定程度解决单点故障问题。
    • 无法完全避免数据不一致,极端网络情况下可能出现部分提交部分回滚。
    • 用于对并发性能有要求、对数据一致性要求相对较低的场景。
  • TCC:将业务操作拆分为 Try、Confirm、Cancel 三个阶段。
    • Try 阶段预留业务资源,Confirm 阶段确认资源完成业务操作,Cancel 阶段在失败时释放资源回滚操作。
    • 可根据业务场景定制开发,性能较高,减少资源占用时间。开发成本高,需实现三个方法,要处理异常和补偿逻辑,实现复杂度大。
    • 适用于对性能要求高、业务逻辑复杂的场景,如电商系统订单处理、库存管理。
  • Saga:将长事务拆分为多个短事务,每个短事务有对应的补偿事务
    • 某个短事务失败,按相反顺序执行补偿事务回滚系统状态。
    • 性能较高,短事务可并行执行减少时间,对业务侵入性小,只需实现补偿事务。
    • 只能保证最终一致性,部分补偿事务失败可能导致系统状态不一致。
    • 适用于业务流程长、对数据一致性要求为最终一致性的场景,如旅游系统订单、航班、酒店预订。
  • 可靠消息最终一致性方案:基于消息队列
    • 业务系统执行本地事务时将业务操作封装成消息发至消息队列,下游系统消费消息并执行操作,失败则消息队列重试。
    • 实现简单,对业务代码修改小,系统耦合度低,能保证数据最终一致性。
    • 消息队列可靠性和性能影响大,可能出现消息丢失或延迟,需处理消息幂等性。
    • 适用于对数据一致性要求为最终一致性、系统耦合度低的场景,如电商订单支付、库存扣减。
  • 本地消息表:业务与消息存储在同一个数据库,利用本地事务保证一致性
    • 后台任务轮询消息表,通过 MQ 通知下游服务,下游服务消费成功后确认消息,失败则重试。
    • 简单可靠,无外部依赖。消息可能重复消费,需幂等设计。
    • 适用场景是异步最终一致性(如订单创建后通知积分服务)。

Seata

  • AT 模式:是 Seata 默认的模式,基于支持本地 ACID 事务的关系型数据库。在 AT 模式下,Seata 会自动生成回滚日志,在业务 SQL 执行前后分别记录数据的快照。当全局事务需要回滚时,根据回滚日志将数据恢复到事务开始前的状态。
  • TCC 模式:需要开发者手动编写 Try、Confirm 和 Cancel 三个方法。Try 方法用于对业务资源进行预留,Confirm 方法用于确认资源并完成业务操作,Cancel 方法用于在业务执行失败时释放预留的资源。
  • SAGA 模式:将一个长事务拆分为多个短事务,每个短事务都有一个对应的补偿事务。当某个短事务执行失败时,会按照相反的顺序执行之前所有短事务的补偿事务,将系统状态回滚到初始状态。

分布式组件

RPC

ZooJeeper

分布式场景

限流算法

分布式一致性算法

Raft 协议

Paxos 协议