文章目录

  • 概念
  • 理论
    • CAP
    • BASE
    • 分布式一致性模型
      • 强一致性
      • 弱一致性
      • 最终一致性
  • 方案
    • 2PC
    • 3PC
    • TCC补偿模式
    • 本地消息表
    • 消息队列
    • 最大努力通知方案
  • 分布式锁的常见解决方案
    • Redis 和 Zookeeper
  • 总结

概念

  事务是单个逻辑单元组成的一组操作,执行这组操作时要么全成功,要么全失败。事务的特性包含一致性、原子性、隔离性和持久性。 原子性是指这组操作是一个整体,要么不执行,要么全部执行完,中间出现任何情况的打断(如意外断电)这组事务都不成功。 隔离性是指两组事务操作同一份数据是互补想干,就像隔离操作似的。 持久性是指事务提交后就永久的修改数据了。 一致性是指事务执行前后原来一致的数据和数据库仍然是一致的。比如订单表里增加了一个订单那么库存表里肯定是减少一份库存的。分布式事务就是在分布式系统中的,组成事务的这一组操作是在不同的节点上,需要保证事务的 AICD 特性。

理论

CAP

  在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)3 个要素最多只能同时满足两个,不可兼得。其中,分区容忍性又是不可或缺的。也就说要么是AP要么是CP。
分区容忍性就是指分布式系统中肯定会出现系统异常,设计时要充分考虑好可能会发生异常,做好解决异常的备用方法。
一致性和上面讲的一致性是同样的意思。
可用性是指无论什么时候不会发生锁表的情况。这也正是A和C不能共存的原因。如果满足了一致性则写表时肯定会锁表,如果满足了可用性则肯定不能锁表。

BASE

BASE理论的核心思想是:
  基本可用(BasicallyAvailable):指分布式系统在出现故障时,允许损失部分的可用性来保证核心可用。
  软状态(SoftState):指允许分布式系统存在中间状态,该中间状态不会影响到系统的整体可用性。
  最终一致性(EventualConsistency):指分布式系统中的所有副本数据经过一定时间后,最终能够达到一致的状态。

分布式一致性模型

强一致性

  数据更新成功后,任意时刻所有副本中的数据都是一致的,一般采用同步的方式实现。

弱一致性

  数据更新成功后,系统不承诺立即可以读到最新写入的值,也不承诺具体多久之后可以读到。

最终一致性

  弱一致性的一种形式,数据更新成功后,系统不承诺立即可以返回最新写入的值,但是保证最终会返回上一次更新操作的值。

方案

2PC

  2PC是基于XA规范实现的,差不多是一回事。属于强一致性模型的一种方案。简单点说就是一组分布式事务操作中每个操作都执行完毕之后再提交。具体如下:
有一个事务管理器,负责协调多个数据库(资源管理器)的事务。协调者询问参与者事务操作是否预执行成功,参与者发回事务执行结果。如果每个数据库都回复ok,即预提交成功,就正式提交事务,在各个数据库开始执行操作,这里失败会有失败异常重试,日志分析,人工重试。否则则把刚才预执行的内容回滚。需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
  这个方案优点是比较简单,但是它只适合单个应用中跨多库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,不适合高并发场景。一般来说某个系统内部如果出现跨多个库的这么一个操作,是不合规的。现在微服务,一个大的系统可能分成几十甚至几百个服务,一般来说,我们的规定和规范,是要求每个服务只能操作自己对应的一个数据库。如果要操作别的服务对应的库,不允许直连别的服务的库,违反微服务架构的规范,随便交叉胡乱访问,几百个服务的话,全体乱套,这样的一套服务是没法管理的,没法治理的,可能会出现数据被别人改错,自己的库被别人写挂等情况。另外缺点还有如下这么些:
  所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
  协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
  在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
  任意一个节点失败就会导致整个事务失败,没有完善的容错机制。

3PC

  3PC 的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一各自的状态。3PC 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。3PC看起来是把 2PC 的提交阶段变成了预提交阶段和提交阶段,但是 3PC 的准备阶段协调者只是询问参与者的自身状况,比如你现在还好吗?负载重不重?这类的。
而预提交阶段就是和 2PC 的准备阶段一样,除了事务的提交该做的都做了。不管哪一个阶段有参与者返回失败都会宣布事务失败。

  3PC相比于2PC的区别:首先准备阶段变成不会直接执行事务,而是会先去询问此时的参与者是否有条件接这个事务,因此不会一来活直接锁资源开始干,使得在某些资源不可用的情况下所有参与者都阻塞着。而预提交阶段的引入起到了一个统一状态的作用,它像一道栅栏,表明在预提交阶段前所有参与者其实还未都回应,在预处理阶段表明所有参与者都已经回应了。假如你是一位参与者,你知道自己进入了预提交状态那你就可以推断出来其他参与者也都进入了预提交状态。但是多引入一个阶段也多一个交互,因此性能会差一些,而且绝大部分的情况下资源应该都是可用的,这样等于每次明知可用执行还得询问一次。2PC里协调者挂在了提交请求还未发出去的时候,那所有参与者都会锁定资源并且阻塞等待着。那么3PC引入了超时机制,参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这一阶段了大概率是提交的,如果是等待预提交命令超时,那该干啥就干啥了,反正本来啥也没干。但是超时机制也会带来数据不一致的问题,比如在等待提交命令时候超时了,参与者默认执行的是提交事务操作,但是有可能执行的是回滚操作,这样一来数据就不一致了。3PC目前没有具体的实现,只是理论上的东西,了解即可。

TCC补偿模式

  TCC的全称是:Try、Confirm、Cancel,是一种最终一致性的方案。其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留。 Confirm 阶段:这个阶段说的是在各个服务中执行实际的操作。Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
这种方案说实话几乎很少人使用,因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大,非常恶心。比如跟钱相关的,支付、交易相关的场景,用 TCC严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,保证在资金上不会出现问题。而且最好是各个业务执行的时间都比较短。但是一般尽量别这么自己手写回滚逻辑或者是补偿逻辑,那个业务代码很难维护。它需要服务调用链必须被记录下来。且每个服务提供者都需要提供一组业务逻辑相反的操作,互为补偿,同时回滚操作要保证幂等。必须按失败原因执行不同的回滚策略。

本地消息表

  A系统在本地一个事务里操作的同时,插入一条数据到消息表,接着A系统将这个消息发送到MQ,B系统接收到消息后,在一个事务里,会先查一下B的本地消息表里是否已经存在相同的操作记录,如果没有则往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理,消息B系统执行成功后,就会更新自己本地消息表的状态以及A系统消息表的状态如果B系统处理失败,那么就不会更新消息表状态,那么此时A系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到MQ中去,让B再处理。这个方案保证了最终一致性,哪怕B事务失败了,但是A会不断重发消息,直到B那边成功为止。但是最大的问题就在于严重依赖于数据库的消息表来管理事务,这个会导致高并发场景无力,难以扩展呢,一般确实很少用

消息队列

  有一些第三方的MQ是支持事务消息的,RocketMQ 就很好的支持了消息事务。拿A给B转账的例子来讲大体的流程是:A先往消息队列发布一个准备的消息,这个消息里包含了一个A提供的可以查询要执行事务状态的接口,然后A查询账户再减去对应的金额,成功后往消息队列发布Commit命令,相当于通知消息队列可以发布消息了,这时订阅了这个消息队列的B就会拿到消息,然后执行增加金额的操作。B执行完后再从消息队里里把这个消息消费掉(可以理解为删掉)。假如A执行失败或由于各种原因无法给消息队列发送Commit命令,一段时间后消息队里会自动调用消息里的查询状态的接口来判断是commit还是rollback。如果B在执行时失败了,则自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿。这个也是最终一致性的解决方案。优点是无序依赖数据库记录状态,缺点是实现难度大,RabbitMQ和Kafka不支持,RocketMQ事务消息部分代码也未开源。

最大努力通知方案

  系统A本地事务执行完后,发送一个消息到MQ,有一专门消费MQ的最大努力通知服务,会消费MQ,然后写入数据库中记录下来,亦可是放入内存队列,接着调用系统B的接口若系统B执行成功就ok;若系统B执行失败,那么最大努力通知服务就定时尝试重新调用系统B,反复N次,最后还是不行才放弃或人工干预。

分布式锁的常见解决方案

  分布式锁目前就三种,一是数据库,二是Redis,三是zookeeper。数据库无论从性能还是具体操作难度上都三个方案中最差的,基本没人用,现在讲一下另外两种.

Redis 和 Zookeeper

  Redis的实现分布式锁的流程大体是设置一个key作为锁,然后设置超时时间,超时或者手动解锁都会释放锁。具体实现方案有两种,一种是先尝试获取锁setnx()再用expire()设置过期时间另一种是直接获取锁并设置过期时间命令是 SET key value NX PX milliseconds ,key是键的名称,value是键的值,EX表示过期时间,单位为秒。也可以用PX以毫秒为单位,SET key value EX second 效果等同于 SETEX key second value 。NX表示只在键不存在时,才对键进行设置操作。SET key value NX 效果等同于 SETNX key value 。 一般都会用第二种方式,因为第一种方式不是原子操作,万一第一步加了锁第二步还没执行就宕机了这个锁就永远锁住了。
  Zookeeper实现分布式锁也是一种方式,此方式用的很少,因为从维护到编程都要麻烦不少且性能上甚至也不如Redis,有兴趣的可以参考这篇文章

总结

  一般比较严格的场景用的是TCC来保证强一致性;然后其他的一些一般的场景基于了阿里的RocketMQ或者是最大努力通知来实现了分布式事务~其实用任何一个分布式事务的这么一个方案,都会导致你那块儿代码会复杂10倍。很多情况下,系统A调用系统B、系统C、系统D,我们可能根本就不做分布式事务。如果调用报错会打印异常日志。每个月也就那么几个bug,很多bug是功能性的,体验性的,真的是涉及到数据层面的一些bug,一个月就几个,两三个?如果你为了确保系统自动保证数据100%不能错,上了几十个分布式事务,代码太复杂;性能太差,系统吞吐量、性能大幅度下跌。99%的分布式接口调用,不要做分布式事务,直接就是监控(发邮件、发短信)、记录日志(一旦出错,完整的日志)、事后快速的定位、排查和出解决方案、修复数据。每个月,每隔几个月,都会对少量的因为代码bug,导致出错的数据,进行人工的修复数据,自己临时动手写个程序,可能要补一些数据,可能要删除一些数据,可能要修改一些字段的值。比你做50个分布式事务,成本要来的低上百倍,低几十倍。要用分布式事务的时候,一定是有成本,代码会很复杂,开发很长时间,性能和吞吐量下跌,系统更加复杂更加脆弱反而更加容易出bug;好处,如果做好了,TCC、可靠消息最终一致性方案,一定可以100%保证你那快数据不会出错。

参考文章:
https://zhuanlan.zhihu.com/p/183753774
https://www.zhihu.com/question/64921387
https://www.cnblogs.com/mayundalao/p/11798502.html

常见的分布式解决方案相关推荐

  1. 分布式事务的特征、原理、以及常见3种解决方案

    分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在这几年越来越火的微服务架构中,几乎可以说是无法避免,本文就围绕分布式事务各方面与大家进行介绍. 事务 1. ...

  2. 直播APP搭建常见难点的解决方案

    直播APP搭建常见难点的解决方案 直播APP搭建方案有很多分类,包括教育直播APP搭建.短视频直播APP搭建.带货直播直播APP搭建.多人互动直播APP搭建等等,类型不同的网络直播APP搭建方案均有差 ...

  3. gulp压缩js转义es6的常见错误及解决方案

    gulp压缩js转义es6的常见错误及解决方案 参考文章: (1)gulp压缩js转义es6的常见错误及解决方案 (2)https://www.cnblogs.com/uimeigui/p/11797 ...

  4. html 跨域_常见跨域解决方案以及Ocelot 跨域配置

    常见跨域解决方案以及Ocelot 跨域配置 Intro 我们在使用前后端分离的模式进行开发的时候,如果前端项目和api项目不是一个域名下往往会有跨域问题.今天来介绍一下我们在Ocelot网关配置的跨域 ...

  5. 为Web程序员解毒:9个IE常见Bug的解决方案

    为Web程序员解毒:9个IE常见Bug的解决方案 http://developer.51cto.com  2009-11-18 09:42  耗子  酷壳  我要评论(0) Web程序员及设计师往往为 ...

  6. 常见跨域解决方案以及Ocelot 跨域配置

    常见跨域解决方案以及Ocelot 跨域配置 Intro 我们在使用前后端分离的模式进行开发的时候,如果前端项目和api项目不是一个域名下往往会有跨域问题.今天来介绍一下我们在Ocelot网关配置的跨域 ...

  7. 串口服务器常见五大问题解决方案

    串口服务器提供串口转网络功能,使得串口设备能够立即具备TCP/IP网络接口功能,连接网络进行数据通信,极大的扩展串口设备的通信距离.为了更方便我们操作和使用,今天飞畅科技的小编来为大家介绍下串口服务器 ...

  8. Android leak内存,GitHub - jin870132/memoryleakdemo: 安卓内存泄露几种常见形式及解决方案...

    安卓内存泄露几种常见形式及解决方案 一.前言 1.内存溢出与内存泄露 内存溢出(oom),是指程序在申请内存时,没有足够的内存空间供其使用,出现oom:比如申请了一个integer,但给它存了long ...

  9. Mac提示app损坏、Error,Mac电脑最常见错误的解决方案

    这篇文章蓝同学给大家分享一下Mac电脑上最常见错误的解决方案. 以下仅给出部分错误提示截图,类似的错误提示还有磁盘映像损坏.xxx.app有啥啥问题.... ①提示xxx.app已损坏,让你移到废纸篓 ...

最新文章

  1. c语言英文字符转数字,C语言常用数字和字符串转换函数(国外英文资料).doc
  2. server2012 图文安装流程
  3. boost::mp11::mp_set_push_back相关用法的测试程序
  4. 向xxxhub发了一个数据包,发现了···
  5. [网络安全自学篇] 三十三.文件上传之绕狗一句话原理和绕过安全狗(六)
  6. python terminator_Python turtle.Terminator方法代碼示例
  7. TF从文件中读取数据
  8. 比较排序算法的时间复杂度 c语言,c语言四种排序算法时间复杂度比较(10页)-原创力文档...
  9. docker 中用docker 启动应用访问docker中的mysql
  10. 浅谈电子数字取证技术
  11. mybatis DATE_FORMAT 格式化时间输出
  12. phpstrom 设置svn提交的代码同步到服务器
  13. 深信服SCSA认证知识点(2)
  14. Linux zlog日志打印
  15. 2018年最新从PayPal提现美金的方法(实战教程)!
  16. Good Luck in CET-4 Everybody! HDU - 1847
  17. 判断输入的日期是一年的第几天或者星期几
  18. 如何删除2345SafeCenterSvc
  19. 一个简洁的背单词、背短语python程序——英文背记系统(自用)
  20. DBG、DMB、DSB 和 ISB

热门文章

  1. R语言使用glmnet包拟合lasso-cox回归模型(生存时间和结果标签)、lasso-cox模型进行特征筛选、plot函数可视化cv.glmnet模型获得的最佳lambda曲线位置及其1个标准差线
  2. 粒子群算法(PSO)的Python实现(求解多元函数的极值)
  3. Nomad 多job/group/task调度测试
  4. MPSOC DP协议介绍
  5. 硬盘三大种类(SSD;HHD;HDD)
  6. 列式数据库和行式数据库的区别
  7. linux正则表达式与文本处理工具
  8. 蓝牙baseband概述
  9. html基础-几种布局
  10. opencv中meanshift和camshift函数的使用