分布式

分布式一致性算法

    • Paxos(帕克索斯)
      • 阶段一
        • (a) Proposer选择一个提案编号N,然后向半数以上的Acceptor发送编号为N的Prepare请求。
        • (b) 如果一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的所有Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给Proposer,同时该Acceptor承诺不再接受任何编号小于N的提案。
      • 阶段二
        • (a) 如果Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value,如果响应中不包含任何提案,那么V就由Proposer自己决定。
        • (b) 如果Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求做出过响应,它就接受该提案。
    • Zab—— 所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,余下的服务器则称为Follower服务器,Leader服务器负责将一个客户端事务请求转化成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器,之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal进行提交
    • Raft——一个节点向其他节点发起投票自己的请求,如果得到大多数票则成为leader;如果得不到最大票数,每个节点随机休眠一段时间,先醒的节点先发请求,后醒的只能投收到的投票请求。

第一阶段 选举:

从集群中选出一个合适的节点作为Leader。

第二阶段 日志同步:

选举出的Leader接收客户端请求,将其转为raft日志。

Leader将日志同步到其他节点,当大多数节点写入成功后,日志变为Committed,一经Committed日志便不会再被篡改。

Leader故障时,切换到第一阶段,重新选举。在Raft算法中,日志冲突时以Leader的日志为准,Follower删除不匹配部分。

    • Gossip——在一个有界网络中,每个节点都随机地与其他节点通信,经过一番杂乱无章的通信,最终所有节点的状态都会达成一致。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,只要这些节可以通过网络连通,最终他们的状态都是一致的。
    • 二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。
    • 三阶段提交:1、引入超时机制。同时在协调者和参与者中都引入超时机制。2、在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
  • 幂等性——WEB资源或API方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的。
    • .MVCC方案——多版本并发控制,该策略主要使用update with condition(更新带条件来防止)来保证多次外部请求调用对系统的影响是一致的。
    • 去重表——在插入数据的时候,插入去重表,利用数据库的唯一索引特性,保证唯一的逻辑。
    • 悲观锁——select for update,整个执行过程中锁定该订单对应的记录。
    • select + insert——核心高并发流程不要用这种方法。
    • 状态机幂等——token机制,防止页面重复提交。采用token加redis(redis单线程的,处理需要排队)
    • 对外提供接口的api如何保证幂等——如银联提供的付款接口:需要接入商户提交付款请求时附带:source来源,seq序列号。source+seq在数据库里面做唯一索引,防止多次付款,(并发时,只能处理一个请求)
  • 分布式系统事务
  • 分布式事务:双阶段、三阶段提交协议。事务管理器预通知各个资源管理器,收到所有资源管理器准备好回应后,再发送提交申请,使所有资源管理器提交。(JTA、Jotm、Automikos、ado.net)。这种方式实现难度不算太高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况。但分布式事务对性能的影响会比较大,不适合高并发和高性能要求的场景。
  • 提供回滚接口:BFF层来协调调用各个服务,有一个服务不成功不再调用其他服务或回滚其他已执行服务。这种实现方式会造成代码量庞大,耦合性高。而且非常有局限性,因为有很多的业务是无法很简单的实现回滚的,如果串行的服务很多,回滚的成本实在太高。
  • 本地消息表:源于ebay,其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。一个本地事务完成后将状态等关键信息写入本地数据库中,通过轮询数据库触发其他事务运行。一种非常经典的实现,基本避免了分布式事务,实现了“最终一致性”。但是,关系型数据库的吞吐量和性能方面存在瓶颈,频繁的读写消息会给数据库造成压力。所以,在真正的高并发场景下,该方案也会有瓶颈和限制的
  • MQ(非事务消息):将某个事务状态信息存在MQ中,通知其他事务运行或回滚。这种方式比较常见,性能和吞吐量是优于使用关系型数据库消息表的方案。如果MQ自身和业务都具有高可用性,理论上是可以满足大部分的业务场景的。不过在没有充分测试的情况下,不建议在交易业务中直接使用。
  • MQ(事务消息):RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。据笔者的了解,各大知名的电商平台和互联网公司,几乎都是采用类似的设计思路来实现“最终一致性”的。这种方式适合的业务场景广泛,而且比较可靠。不过这种方式技术实现的难度比较大。目前主流的开源MQ(ActiveMQ、RabbitMQ、Kafka)均未实现对事务消息的支持,所以需二次开发或者新造轮子。比较遗憾的是,RocketMQ事务消息部分的代码也并未开源,需要自己去实现。
  • 多次发送确认消息:多次访问幂等接口,打到最终一致性。
  • DTS

全局唯一ID

  • Twitter 方案(Snowflake 算法):41位时间戳+10位机器标识(比如IP,服务器名称等)+12位序列号(本地计数器)
  • Flicker 方案:MySQL自增ID + "REPLACE INTO XXX:SELECT LAST_INSERT_ID();"
  • UUID:缺点,无序,字符串过长,占用空间,影响检索性能。
  • MongoDB 方案:利用 ObjectId。缺点:不能自增。
  • TDDL 在分布式下的SEQUENCE:在数据库中创建 sequence 表,用于记录,当前已被占用的id最大值。每台客户端主机取一个id区间(比如 1000~2000)缓存在本地,并更新 sequence 表中的id最大值记录。客户端主机之间取不同的id区间,用完再取,使用乐观锁机制控制并发。

分布式锁

实现分布式锁有以下几个方式:

  • MySQL
  • ZK(Curator)
  • Redis(Redission)
  • 自研分布式锁:如谷歌的 Chubby。

mysql 实现方式是新建一个锁表,利用悲观锁或乐观锁进行访问,没有就插入新数据,可重入锁就加1,获取锁阻塞通过死循环实现,锁超时通过比较超时时间来实现。解锁的话就把相应记录删除。通过事务保证其原子性。

ZK的实现方式是新建一个有序临时节点。如果顺序为最小,则获得锁,未获得锁的线程对节点注册监听,当锁释放即节点连接断开,尝试再次获取锁。Curator 封装了 ZooKeeper 底层的 API,使我们更加容易方便的对 ZooKeeper 进行操作,并且它封装了分布式锁的功能。InterProcessMutex 是 Curator 实现的可重入锁。Curator 提供了读写锁,其实现类是 InterProcessReadWriteLock。

Redis 分布式锁

对于某个资源加锁我们只需要:setNx(set if not exist) 

setNx resourceName value

加锁了之后如果机器宕机那么这个锁就不会得到释放所以会加入过期时间,加入过期时间需要和 setNx 同一个原子操作。

Redis 2.8 之后 Redis 支持 nx 和 ex 操作是同一原子操作。

set resourceName value ex 5 nx

Redission 也是 Redis 的客户端,相比于 Jedis 功能简单。Jedis 简单使用阻塞的 I/O 和 Redis 交互,Redission 通过 Netty 支持非阻塞 I/O。

Redission 封装了锁的实现,其继承了 java.util.concurrent.locks.Lock 的接口,让我们像操作我们的本地 Lock 一样去操作 Redission 的 Lock。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值