分布式

大约 9 分钟

分布式

👍🏻 凤凰架构open in new window

1. 分布式锁

深度剖析:Redis分布式锁到底安全吗?看完这篇文章彻底懂了!open in new window

Redisson-8.分布式锁和同步器open in new window

分布式锁实现多个进程对共享资源的互斥。实现方式上可以采用第三方中间件来做,如MySQLZooKeeperRedisetcd

1.1 基于关系型数据库(MySQL)的分布式锁

悲观锁: select id from t where col = xx for update,会一直阻塞到事务提交,

乐观锁: select id,old_version from t where col = xxupdate t set ver = old_version+1 where col=xx and ver=old_verison

1.2 基于Redis的分布式锁

1.2.1 setnx lock vdel lock

加锁时客户端1加锁成功 SETNX lock 1 => 1,客户端2加锁失败SETNX lock 1 => 0

解锁时客户端1 del lock => 1

存在的问题: 当客户端1中没能释放锁(如业务异常或进程崩溃),导致其他客户端一直都拿不到锁,导致死锁 (这种情况需要人工介入解锁)

1.2.2 set lock v ex n nx

为了避免死锁的问题,添加过期时间自动释放锁,用一行命令保证原子性,如set lock 1 ex 10 nx => OK,其他客户端尝试加锁时返回(nil)

存在的问题:

A. 过期时间可能不准确,设置少了导致提前释放锁,设置多了会加锁时间过长(一般没这个问题,锁可以释放时可以指定del lock,但是会导致B问题),

B. 一个客户端可能释放了其他客户端锁持有的锁

1.2.3 set lock $uuid ex n nx

对于客户端释放其他客户端持有锁的问题,可以将val设置为一个随机且唯一的值(只有加锁的客户端知道,也可以是线程号)

加锁时: set lock 45r8iu ex 10 nx

解锁时: 采用Lua脚本保证原子性,先判断是否为当前客户端加锁,是的话进行删除

// 判断锁是自己的,才释放
if redis.call("GET",KEYS[1]) == ARGV[1]
then
    return redis.call("DEL",KEYS[1])
else
    return 0
end

1.2.4 使用Redissonopen in new window

为了解决锁可能提前过期的问题,可以在加锁时设置过期时间,然后通过守护线程定时检测锁的失效时间,如果快过期了但是业务操作还未完成,就自动进行续期

此时,只通过Redis解决分布式锁的方案如下:

  1. 🔐加锁时,采用set lock $uuid ex 10 nx,来保证互斥性,自动失效性,只能加锁客户端解锁,并且开启守护线程检测失效,根据业务决定是否需要续期
  2. 🔓解锁时, 通过Lua脚本保证原子性,先判断是否为客户单加锁,是的话才解锁

Redisson的分布式锁原理是怎样的?

加锁:

Redisson首先通过hash选择一个redis节点,然后执行加锁的Lua脚本来保证原子性,设置一个hash结构的锁 hset lock $uuid:1 1,其中$uudi:1表示完成了加锁,val的1表示加锁加了一次(可重入),

当其他客户段想获取锁时执行同样的脚本先判断key lock是否存在,在判断是否有客户端2的ID,以及锁的剩余时间,进行自旋,

在加锁成功的同时会启用一个watch dog的后台线程,每隔10秒进行检查,判断是否需要续期

解锁:

由于是可重入锁,当val值为0时表示不再持有锁,通过pub/sub的方式进行释放锁执行del lock

1.2.5 RedLock

RedLock的大概流程是先有多于5个的Redis实例,然后分别向各个实例请求加锁,当大于半数加锁成功就认为是加锁成功,释放锁时操作所有节点

但是要考虑加锁的耗时,网络等原因

1.3 基于ZooKeeper的分布式锁

ZooKeeper实现的分布式锁:

  1. 客户端1创建临时节点(会产生惊群效应,一般使用临时有序节点) 如/lock
  2. 客户端2尝试创建临时节点失败,加锁失败,
  3. 客户端1操作共享资源完成后,删除/lock节点,释放锁,客户端2通过watcher机制发现可以加锁

可能有的问题: ZooKeeper长时间收不到客户端的心跳(例如GC或者网络延迟),也会把临时节点删除,导致其他客户端提前拿到了锁,但是原客户端认为自己有锁

ZooKeeper和Redis实现分布式锁的优劣

ZooKeeper实现简单,不需要考虑锁的过期时间,通过watcher加锁失败可以等待锁的释放,实现乐观锁,

但是ZooKeeper的部署和运维成本高,性能上不如Redis,也存在着客户端与ZooKeeper长时间失联导致的锁提前释放的问题

2. 分布式缓存

前端有页面和浏览器缓存,Application Cache,localStore等,

网络传输缓存,会有多层缓存,CDN,负载均衡,

服务端缓存,本地缓存:Guava,Map,Caffine,外部缓存大多数服务端缓存采用redis,

数据库缓存,MyBtais的一二级缓存,MySQL的缓存

2.1 Redis的持久化,高可用,过期策略,缓存异常问题,缓存和数据库双写一致性问题

https://spectred.github.io/interview/redis.htmlopen in new window

3. 分布式事务

分布式事务指多个服务同时访问多个数据源的事务处理机制。主流的方案有2PC,3PC,TCC,可靠事件队列,SAGA事务

3.1 2PC 两阶段提交

存在协调者和参与者,准备阶段: 协调者询问参与者是否可正常执行,参与者执行事务但不提交;提交阶段: 协调者通知参与提交,参与者进行提交,如果有失败则整体回滚

存在的问题: 资源被同步阻塞,协调者单点故障

典型应用: MySQL的主从复制

MySQL使用2PC,内部自动将普通事务当做一个XA事务(内部分布式事务)来保证binlogredolog的一致性:

  • Commit会被自动分成Prepare和Commit两个阶段
  • binlog会被当做事务协调者,binlog event会被当做协调者日志

3.2 3PC 三阶段提交

为解决2PC的同步阻塞问题,3PC在协调者和参与者中都引入了超时机制。

CanCommit: 协调者询问参与者是否可正常执行,参与者预估判断是否可执行,不做事务操作

PreCommit: 协调者询问参与者是否可正常执行,参与者执行事务但不提交

DoCommit: 协调者向所有参与者发起事务提交通知,参与者收到通知或者超时提交事务向协调者反馈结果

改进内容: 引入超时机制,添加预提交阶段

存在的问题: 第三阶段中,如果参与者收到了PreCommit消息后,出现了不能正常通信的问题,参与者依然会提交事务导致数据不一致

3.3 TCC

TCC基于业务层面的事务定义,锁粒度由业务自己控制,为了解决复杂业务中跨表跨库等大粒度资源锁定的问题。

TCC把事务运行过程分成 Try,Confirm/Cancel两个阶段,每个阶段逻辑由业务控制,避免长事务,可以获取更高性能

Try: 调用Try接口,尝试执行业务,完成所有业务检查,预留业务资源

Confirm/Cancel:两者互斥,只能进入其中一个,并且都满足幂等性,允许失败重试

  • Confirm: 对业务系统做确认提交,确认执行业务操作,不做其他业务检查,只使用Try阶段预留的业务资源
  • Cancel: 在业务执行错误,需要回滚的状态下执行业务取消,释放预留资源

缺点: 侵入型强,事务管理器需要记录事务日志损耗性能

3.4 基于MQ的可靠消息投递方案(最大努力交付)

  1. 提交本地事务,向MQ发送消息,并将消息状态落库到消息表,

  2. 其他服务消费消息后提交本地事务,更新消息表,双向确认,

  3. 如果有问题需要手动/定时批量扫描进行补偿

支持分布式事务的消息框架如RocketMQ原生就支持分布式事务操作

3.5 SAGA事务

将一个大事务拆分成若干个小事务T1,T2...Tn,每个子事务设计对应的补偿动作C1,C2...Cn

有两种恢复策略

  • 正向恢复: 如果其中一个子事务提交失败,则对这个子事务一直重试至成功,不需要补偿 ,例如扣款了就要发货
  • 反向恢复: 如果其中一个子事务提交失败,则一直执行对应的补充动作进行补偿直至成功

3.6 ⭐️Seataopen in new window

Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务

4. 分布式消息队列

https://spectred.github.io/interview/kafka.htmlopen in new window

5. 分布式存储

5.1 读写分离

适用于读多写少

为了不让数据库的读成为业务瓶颈,同时也为了保证写库的成功率,一般采用读写分离保证。可一主一从,一主多从

MySQL的主从复制open in new window

5.2 分库分表

单表行数超过500万行或者单表容量超过2GB,才推荐分库分表

分库分表open in new window

分库分表后,会引入分布式事务的问题,跨库关联查询(字段冗余),跨库跨表的合并和排序(依赖分库分表中间件)

6. 分布式基础理论

6.1 CAP理论

CAP理论描述了在一个分布式系统中,涉及共享数据问题时,以下三个特性最多只能同步满足其中两个:

  • Consistency 一致性: 数据在任何时刻任何分布式节点中所看大的都是符合预期的
  • Availability 可用性: 系统不间断地提供服务能力
  • Partition Tolerance 分区容忍性: 分布式环境中部分节点因网络原因而彼此失联后,系统仍能正确的提供服务的能力

6.2 BASE达成最终一致性

ACID保证强一致性

  • Basically Available 基本可用性
  • Soft State 柔性事务
  • Eventually Consistent 最终一致性

6.3 分布式共识

目前面试还没被问过,被问到时再补充吧...😄

6.3.1 Paxos

6.3.2 Multi Paxos

6.3.3 Gossip

6.3.4 Raft