分布式

57 阅读6分钟

一、分布式理论

CAP

什么是CAP?

  • C:Consistency 一致性
  • A:Availability 可用性
  • P:Partition Tolerance 分区容错性

一个分布式系统不可能同时满足数据一致性服务可用性分区容错性这三个基本需求,最多只能同时满足其中的两个。

一致性(C)

一致性是指强一致性(Strong Consistency),又叫线性一致性(Linearizable Consistency),它要求多节点组成的分布式系统,能像单节点一样运作,如果一个写操作返回成功,那么之后的读请求都必须读到这个新数据;如果返回失败,那么所有的读操作都不能读到这个数据。

一致性中除了强一致性之外,还有其他的一致性级别,比如序列一致性( Sequential Consistency )和最终一致性( Eventual Consistency )等。

所有节点读的数据一致,而不是节点A读1,节点B读0;

可用性(A)

系统提供的服务必须处于100%可用的状态,对于用户的每一个操作请求,系统总能够在有限的时间内返回结果。

分区容错性(P)

分区指的是在整个分布式系统中,因为各种网络原因,系统被分隔成多个单独的部分,它不仅包含我们通常说的网络分区,也包含因为网络丢包导致的网络不通的情况。并且,这里说的因为网络丢包导致网络不通的情形,还包含节点宕机的场景,由于系统的其他机器不知道某个节点宕机了,只知道与宕机节点的网络是不通的,所以当节点宕机发生时,其他节点发往宕机节点的包也将丢失。

在任何网络分区下,不影响服务的正常工作;

二、分布式事务

什么是分布式事务,相关协议及实现方式是什么?

  • 分布式事务用于在分布式系统中保证不同节点之间的数据一致性;
  • 要权衡CAP,即一致性可用性分区容错性,分区容错性是必须要保证的,所以主要权衡一致性和可用性,即CP或AP;
  • 最具有代表性的是由Oracle Tuxedo系统提出的XA分布式事务协议;

    XA协议包含二阶段提交(2PC)三阶段提交(3PC)两种实现。

  • 二阶段提交、TCC、异步确保型、事务型消息
  • raft同步可以保证节点集群的一致性和性能平衡,Zookeeper为典型例子;

二阶段提交的过程是怎样的?三阶段提交呢?

  • 二阶段提交包括preparecommit两个阶段;
  • 二阶段提交牺牲了可用性,保持了强一致性,非常影响性能;
  • 首先由事务管理器向分布式服务发送prepare消息,待所有服务返回给事务管理器确认准备完成后,进行第二步,由事务管理器发送commit消息,待所有服务器返回执行完成消息,完成二阶段提交;
  • 如果其中任何一个commit操作失败了,则其他所有成功commit操作都要回滚,此时牺牲了可用性,请求失败的服务器会被阻塞;
  • 三阶段提交是在二阶段提交的基础上增加了超时机制,如果第二阶段返回ack超时,则会发送回滚commit;

TCC协议的过程是怎样的?

  • TCC无法保证强一致性,通过状态去控制流程;
  • TCC包括try、confirm、cancel三部分,每个模块顺序执行完try然后执行confirm,如果出现回滚,则顺序执行cancel;
  • 事务独立,回滚时顺序回滚;
  • 业务之间独立性较差,问题不好排查;

什么是raft同步,怎么实现的?

  • 用以平衡集群中数据的一致性和性能;
  • 主要配合超过一半以上的数字去做取舍,只要n/2+1节点成功返回了ack,则对外表现为同步完成;
  • 主要是为了主节点挂掉的时候,其他从节点进行选主,可以选出最新的已经同步过的从节点;

什么是异步确保型?

  • 牺牲强一致,确保最终一致性;
  • 采用异步消息的方式确保事务最终一致;
  • 保持业务独立性,主业务无感知,可通过消息日志查看问题,且中间件自带重试等机制;

什么是事务型消息?

  • 首先向消息中间件中添加prepare消息,中间件会持久化到磁盘,但是不会去推送这个消息;
  • 等本地事务commit之后,向中间件发送一条commit消息,带上之前的prepare消息ID,中间件才会发送对应的消息;
  • 中间件会有回查机制,如果中间件一直没有收到commit消息,则中间件会会查消息发送方,根据发送方的状态去处理这条prepare消息;

三、分布式锁

如何实现一个分布式锁?

  • 基于数据库:利用主键唯一的特性(类全路径名+方法名作为主键),如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可。易于理解,但是可能出现单点故障,性能也可能成为瓶颈。
  • 基于Redis:主要利用redis的setnx()和expire()方法,还有tryLock()和lock()方法等,目前比较流行用Redission实现。

setnx (lockkey, 1) 如果返回 0,则说明占位失败;如果返回 1,则说明占位成功;

expire () 命令对 lockkey 设置超时时间,为的是避免死锁问题;

执行完业务代码后,可以通过 delete 命令删除 key。

  • 基于Zookeeper:利用临时节点与 watch 机制。每个锁占用一个普通节点 /lock,当需要获取锁时在 /lock 目录下创建一个临时节点,创建成功则表示获取锁成功,失败则 watch/lock 节点,有删除操作后再去争锁。临时节点好处在于当进程挂掉后能自动上锁的节点自动删除即取消锁。

Redis lock和unlodk的区别

返回值:lock()没有返回值;tryLock()的返回值是boolean。

时机:lock()一直等锁释放;tryLock()获取到锁返回true,获取不到锁并直接返回false。

中断性:lock()不可以被中断;tryLock()是可以被打断的,被中断的。

Redisson是怎么实现分布式锁的?

image.png