分布式事务

来源:互联网 发布:剑灵 知乎 编辑:程序博客网 时间:2024/06/06 03:55

eg:跨行转账

     第一步扣款建设银行账户的1000元     第二步通知招商银行账户上加1000
  • 产生的原因
  • 数据库分库分表
    当单表一年的数据超过1000万条,那么就要考虑分库分表。就是一个数据库变成了多个数据库.如果一个操作涉及到01库、02库,那么就要考虑到数据的一致性
    这里写图片描述

    1. 业务拆分
      原来的单机撑起了整个电商系统,现在对整个电商进行拆解,分离出了订单中心、用户中心、库存中心等.他们都有对应的数据库.如果同时对订单和库存进行操作,那么就设计到订单数据库和库存数据库.
      这里写图片描述
  • 事务特性

    1. 原子性: 整个事务中的所有操作,要么全部完成,要么全部失败。
    2. 一致性:必须保证系统的一致性。A有500元,B有300元。在一个事务里A转给B50,不论斌方法多少。最后A账户一定时450,B是350.这就是分布式锁
    3. 隔离性:事务与失误之间不会相互影响,一个事务的中间状态不会被其他事务感知.
    4. 持久性:一个事务完成了。那么数据的变更就在数据库中l

==================

  • 应用场景

    1. 支付
      扣除买家数据库,对卖家数据库进行增加
    2. 在线下单
      买家在电商下单,一个是库存,一个是更新订单信息。库存和订单属于不同的数据库.
  • 常见的解决方案

    1. 基于XA协议的两阶段提交
      这里写图片描述
      但明显在高并发的环境下,性能不理想
    2. 消息事务+最终一致性
      基于消息中间件的两阶段提交,将本地事务和发消息放在了一个分布式事务里,保证要不本地操作成功并且对外发消息成功,要么两者失败.RocketMQ
      这里写图片描述
      1出错,则整个事务失败,不会执行A的本地操作
      2出错,则整个事务失败,不会执行A的本地操作
      3出错,这时候需要回滚预备消息,怎么回滚?答案是A系统实现一 个消息中间件的回调接口,消息中间件会去不断执行回调接口,检查A事务执行是否执行成功,如果失败则回滚预备消息
      4 出错,这时候A的本地事务是成功的,那么消息中间件要回滚A吗?答案是不需要,其实通过回调接口,消息中间件能够检查到A执行成功了,这时候其实不需要A发提交消息了,消息中间件可以自己对消息进行提交,从而完成整个消息事务

上述保存消息的方式使得消息数据和业务数据紧耦合在一起,从架构上看不够优雅,而且容易诱发其他问题。为了解耦,可以采用以下方式。

  1)用户A在扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送,只有消息发送成功后才会提交事务;

  2)当用户A扣款事务被提交成功后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送该消息;

  3)当用户A扣款事务提交失败回滚后,向实时消息服务取消发送。在得到取消发送指令后,该消息将不会被发送;

  4)对于那些未确认的消息或者取消的消息,需要有一个消息状态确认系统定时去用户A系统查询这个消息的状态并进行更新。为什么需要这一步骤,
举个例子:假设在第2步用户A扣款事务被成功提交后,系统挂了,此时消息状态并未被更新为“确认发送”,从而导致消息不能被发送。

  优点:消息数据独立存储,降低业务系统与消息系统间的耦合;

  缺点:一次消息发送需要两次请求;业务处理服务需要实现消息状态回查接口。

4.2 如何解决消息重复投递的问题

  还有一个很严重的问题就是消息重复投递,以我们用户A转账到用户B为例,如果相同的消息被重复投递两次,那么我们用户B账户将会增加2万而不是1万了。

  为什么相同的消息会被重复投递?比如用户B处理完消息msg后,发送了处理成功的消息给用户A,正常情况下用户A应该要删除消息msg,但如果用户A这时候悲剧的挂了,
重启后一看消息msg还在,就会继续发送消息msg。

  解决方法很简单,在用户B这边增加消息应用状态表(message_apply),通俗来说就是个账本,用于记录消息的消费情况,每次来一个消息,
在真正执行之前,先去消息应用状态表中查询一遍,如果找到说明是重复消息,丢弃即可,如果没找到才执行,同时插入到消息应用状态表(同一事务)。
3.JTA
作为Java平台上事务规范JTA(Java Transaction API)也定义了对XA事务的支持,实际上,JTA是基于XA架构上建模的,在JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即JTS)实现。像很多其他的java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供,目前JTA的实现主要由以下几种:

1.J2EE容器所提供的JTA实现(JBoss)
2.独立的JTA实现:如JOTM,Atomikos.这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。如Tomcat,Jetty以及普通的java应用

0 0
原创粉丝点击