分布式事务

本地事务会出现的问题

1、远程服务假失败

        远程服务其实成功了,由于网络故障等没有返回,导致订单回滚,库存扣减

2、远程服务执行完成,下面其他方法出现问题,导致已执行的其他远程请求不能回滚。

 伪代码:


                //TODO 3、保存订单
                saveOrder(order);
                //、库存锁定,只要有异常回滚订单数据
                //订单号,所有订单项(skuId,skuName,num)
                WareSkuLockVo lockVo = new WareSkuLockVo();
                lockVo.setOrderSn(order.getOrder().getOrderSn());
                List<OrderItemVo> locks = order.getOrderItems().stream().map(item -> {
                    OrderItemVo itemVo = new OrderItemVo();
                    itemVo.setSkuId(item.getSkuId());
                    itemVo.setCount(item.getSkuQuantity());
                    itemVo.setTitle(item.getSkuName());
                    return itemVo;
                }).collect(Collectors.toList());
                lockVo.setLocks(locks);
                //TODO 4、远程锁库存
                R r = wmsFeignService.orderLockStock(lockVo);
                if(r.getCode()==0){

                    //锁成功
                    response.setOrder(order.getOrder());

                    //TODO 5、远程扣减积分
                    int i=10/0;
                    return response;
                }else{
                    //锁失败
                   String msg=(String)r.get("msg");
                    throw  new NoStockException(4L);
                    //return response;
                }

2、本地事务的基本性质

        原子性(Atomicity ) 、一致性 ( Consistency ) 、隔离性或独立性 ( Isolation)
和持久性 (Durabilily) ,简称就是 ACID
   
原子性:一系列的操作整体不可拆分,要么同时成功,要么同时失败
一致性:数据在事务的前后,业务整体一致。
 
隔离性:事务之间互相隔离。
持久性:一旦事务成功,数据一定会落盘在数据库。
 
使用同一条连接操作不同的数据表,一旦有异常, 我们可以很容易的整体回滚

3、事务的隔离级别

READ UNCOMMITTED (读未提交)
 
该隔离级别的事务会读到其它未提交事务的数据,此现象也称之为脏读。
 
READ COMMITTED (读提交-更新)
 
一个事务可以读取另一个已提交的事务,多次读取会造成不一样的结果,此现象称为不可重
复读问题, Oracle SQL Server 的默认隔离级别。
 
REPEATABLE READ (可重复读-插入)
 
该隔离级别是 MySQL 默认的隔离级别,在同一个事务里, select 的结果是事务开始时时间
点的状态,因此,同样的 select 操作读到的结果会是一致的,但是,会有幻读现象。 MySQL
InnoDB 引擎可以通过 next-key locks 机制(参考下文 " 行锁的算法 " 一节)来避免幻读。
 
SERIALIZABLE (序列化)
 
在该隔离级别下事务都是串行顺序执行的, MySQL 数据库的 InnoDB 引擎会给读操作隐式
加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题。

以上4是事务级别的并发能力依次降低。

4、事务的传播行为

        总结一句话就是 子继承父 。不是自己的儿子。可以有自己的特性

1 PROPAGATION_REQUIRED 如果当前没有事务,就创建一个新事务,如果当前存在事务,
就加入该事务,该设置是最常用的设置。
2 PROPAGATION_SUPPORTS 支持当前事务,如果当前存在事务,就加入该事务,如果当
前不存在事务,就以非事务执行。
3 PROPAGATION_MANDATORY 支持当前事务,如果当前存在事务,就加入该事务,如果
当前不存在事务,就抛出异常。
4 PROPAGATION_REQUIRES_NEW 创建新事务,无论当前存不存在事务,都创建新事务。
5 PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当
前事务挂起。
6 PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
7 PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,
则执行与 PROPAGATION_REQUIRED 类似的操作。

伪代码:

  @Transactional
    public void a(){

    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void b(){

    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void c(){

    }

前面还有一个坑就是

        同一个对象内事务方法互调默认失效,原先绕过了代理对象,事务使用代理对象来控制的。

不同XXXservice调用不同方法才生效

        

 @Transactional
    public void a(){

        bService.b();
        cService.c();
    }
    @Transactional(propagation = Propagation.REQUIRED)
    public void b(){

    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void c(){

    }

解决:使用代理对象来调用事务方法

        1)、引入spring-boot-starter-aop,引入了aspectj

        2)、@EnableAspectJAutoProxy(exposeProxy = true) ;开启了aspectj 动态代理功能;以后所有的动态代理都是aspectj创建的(即使没有接口也能创建动态代理)

        3)、用代理对象调用 

分布式事务

1、为什么会出现分布式事务

        分布式系统经常出现的异常
机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的 TCP 、存储数据丢失 ...

 节点之间互相的状态不能同步,网络状况,数据互相感知不到

 2、CAP定理和BASE理论

        1)、一致性(Consistency)

                在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访
问同一份最新的数据副本)

                (在分布式系统下。所有的数据在同一时候是否是同样的值)

        2)、可用性(Availability)

        在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据
更新具备高可用性)
                (在分布式系统下。某台机器发生故障,其他服务是否继续相应客户的操作)

        3)、分区容错性(Partition tolerance)

                大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。
分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务
器放在美国,这就是两个区,它们之间可能无法通信。

                  (一台在北京,一台在上海,网络区不同,可能这两个区不能通信怎么办)

        CAP原则指的是,这三个要素最多只能同时实现两个,不可能三者兼顾。

而在分布式系统下一定要满足分区容错性,因为网络肯定会出现问题。

而在P的基础上如果满足A可用想就会出现c的错误

例如:3台服务。如果A,B,C。出现分区容错性.满足可用性的话。客户能同时访问A,B,C。但是由于出现了分区容错性。那么这个三个直接的数据就不是一致性的

同理:3台服务器,如果A,B,C.。出现了分区容错性。满足数据一致性的话。就必须不能让这3台服务器都相应。

分布式系统中实现一致性的 raft 算法、
        通俗的讲就是选择领导:随从,候选者,领导。在从随从变为候选者的中间没一个人有一个自旋时间(150-300毫秒)。最先完成的就是候选者,然后投自己一票。再发送消息给还没变成候选者的人,让他们投票给自己为领导。这样候选者就变成了领导。领导发送给随从的时候有一个心跳时间。就这样一直维持心跳。心跳时间不能超过自旋时间。
当领导挂掉。那和自旋时间快那个就成为领导
而数据直接同步是领导者发送消息。在领导接受到消息的后,发下一次发送心跳消息会带上消息去同步他的随从
出现分区容错性后,没个区选择自己的领导,保存客户端发送的数据,当分区回复后。两个领导根据最多轮选择出来的领导当最后恢复后的新领导。而另外一个区的数据去同步新领导区的数据。老领导区的数据新加的回滚。

 

 

 Raft Consensus Algorithm  --更详细的可以查看此网站

3、BASE理论

是对 CAP 理论的延伸,思想是即使无法做到强一致性( CAP 的一致性就是强一致性),但可
以采用适当的采取弱一致性,即 最终一致性
BASE 是指
        基本可用(Basically Available
基本可用是指分布式系统在出现故障的时候,允许损失部分可用性(例如响应时间、
功能上的可用性),允许损失部分可用性。需要注意的是,基本可用绝不等价于系
统不可用。
         响应时间上的损失:正常情况下搜索引擎需要在 0.5 秒之内返回给用户相应的
查询结果,但由于出现故障(比如系统部分机房发生断电或断网故障),查询
结果的响应时间增加到了 1~2 秒。
功能上的损失:购物网站在购物高峰(如双十一)时,为了保护系统的稳定性,
部分消费者可能会被引导到一个降级页面。
        软状态( Soft State
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布
式存储中一般一份数据会有多个副本,允许不同副本同步的延时就是软状态的体
现。 mysql replication 的异步复制也是一种体现。
        最终一致性( Eventual Consistency
最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状
态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

强一致性、弱一致性、最终一致性
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了
不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是 强一
致性 。如果能容忍后续的部分或者全部访问不到,则是 弱一致性 。如果经过一段时间后要求
能访问到更新后的数据,则是 最终一致性

分布式事务几种方案

        1、)2PC模式

数据库支持的 2PC 2 phase commit 二阶提交】,又叫做 XA Transactions
MySQL 5.5 版本开始支持, SQL Server 2005 开始支持, Oracle 7 开始支持。
其中, XA 是一个两阶段提交协议,该协议分为以下两个阶段:
第一阶段:事务协调器要求每个涉及到事务的数据库预提交 (precommit) 此操作,并反映是
否可以提交 .
第二阶段:事务协调器要求每个数据库提交数据。
其中,如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务
中的那部分信息。

 
XA 协议比较简单,而且一旦商业数据库实现了 XA 协议,使用分布式事务的成本也比较
低。
XA 性能不理想 ,特别是在交易下单链路,往往并发量很高, XA 无法满足高并发场景
XA 目前在商业数据库支持的比较理想, mysql 数据库中支持的不太理想 mysql
XA 实现,没有记录 prepare 阶段日志,主备切换回导致主库与备库数据不一致。
许多 nosql 也没有支持 XA ,这让 XA 的应用场景变得非常狭隘。
也有 3PC ,引入了超时机制(无论协调者还是参与者,在向对方发送请求后,若长时间
未收到回应则做出相应处理)

2)、柔性事务-TCC事务补偿型方案

就是有回滚的方法

刚性事务:遵循 ACID 原则,强一致性。
柔性事务:遵循 BASE 理论,最终一致性;
与刚性事务不同,柔性事务允许一定时间内,不同节点的数据不一致,但要求最终一致。

一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
二阶段 commit 行为:调用 自定义 的 commit 逻辑。
二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

3)、柔性事务-最大努力通知型方案

就是主流程里的各个分支去订阅消息。那个分支错误。就发送消息到消息队列主流程失败。各分支去监听消息,然后各自回滚。怕有分支因为各种原先收不到消息。会隔几秒发送消息到队列。主流程消息错误。等待分支正常。就收到消息后。返回接受到消息了。不需要通知了。就结束。这就是最大努力通知型方案。

按规律进行通知, 不保证数据一定能通知成功,但会提供可查询操作接口进行核对 。这种
方案主要用在与第三方系统通讯时,比如:调用微信或支付宝支付后的支付结果通知。这种
方案也是结合 MQ 进行实现,例如:通过 MQ 发送 http 请求,设置最大通知次数。达到通
知次数后即不再通知。
案例:银行通知、商户通知等(各大交易业务平台间的商户通知:多次通知、查询校对、对
账文件),支付宝的支付成功异步回调

4)、柔性事务+可靠消息+最终一致性方案(异步确保型)

实现:业务处理服务在业务事务提交之前,向实时消息服务请求发送消息,实时消息服务只
记录消息数据,而不是真正的发送。业务处理服务在业务事务提交之后,向实时消息服务确
认发送。只有在得到确认发送指令后,实时消息服务才会真正发送。

防止消息丢失:
/**
* 1 、做好消息确认机制( pulisher consumer 【手动 ack 】)
* 2 、每一个发送的消息都在数据库做好记录。定期将失败的消息再次发送一
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追逐路上的小人物

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值