关于分布式事务

学习ACE时,好多考题涉及到了RocketMQ的知识,于是乎找来阿里云大学的视频看。在看RocketMQ的功能的时候,提到了事务消息,其目的是帮助用户实现类似X/Open XA的分布事务功能,通过MQ事务消息能达到分布式事务的最终一致。

对分布式事务不太理解,所以连带事务一起走一遍一下。
 

1、事务

1.1、事务的ACID语义

ACID即:原子性、一致性、隔离性、持久性

1.1.1、原子性

举个例子:

A想要从自己的帐户中转1000块钱到B的帐户里。那个从A开始转帐,到转帐结束的这一个过程,称之为一个事务。在这个事务里,要做如下操作:

  •  1. 从A的帐户中减去1000块钱。如果A的帐户原来有3000块钱,现在就变成2000块钱了。
  •  2. 在B的帐户里加1000块钱。如果B的帐户如果原来有2000块钱,现在则变成3000块钱了。

如果在A的帐户已经减去了1000块钱的时候,忽然发生了意外,比如停电什么的,导致转帐事务意外终止了,而此时B的帐户里还没有增加1000块钱。那么,我们称这个操作失败了,要进行回滚。回滚就是回到事务开始之前的状态,也就是回到A的帐户还没减1000块的状态,B的帐户的原来的状态。此时A的帐户仍然有3000块,B的帐户仍然有2000块。

我们把这种要么一起成功(A帐户成功减少1000,同时B帐户成功增加1000),要么一起失败(A帐户回到原来状态,B帐户也回到原来状态)的操作叫原子性操作。

如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫原子性。

     实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。
     回滚实际上是一个比较高层抽象的概念,大多数DB在实现事务时,是在事务操作的数据快照上进行的(比如,MVCC),并不修改实际的数据,如果有错并不会提交,所以很自然的支持回滚。
     而在其他支持简单事务的系统中,不会在快照上更新,而直接操作实际数据。可以先预演一遍所有要执行的操作,如果失败则这些操作不会被执行,通过这种方式很简单的实现了原子性。

1.1.2、一致性

     一致性是指事务使得系统从一个一致的状态转换到另一个一致状态。事务的一致性决定了一个系统设计和实现的复杂度。事务可以不同程度的一致性:

  •      强一致性:读操作可以立即读到提交的更新操作。
  •      弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。
  •      最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。

     其他一致性变体还有:

  •      单调一致性:如果一个进程已经读到一个值,那么后续不会读到更早的值。
  •      会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。

1.1.3、隔离性

并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。在事务并发操作时,可能出现的问题有:

  • 脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
  • 不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这会导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
  • 幻读:在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

事务的隔离级别从低到高有:

  • Read Uncommitted:最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
  • Read Committed:只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
  • Repeated Read:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
  • Serialization:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。

通常,在工程实践中,为了性能的考虑会对隔离性进行折中。

1.1.4、持久性

事务提交后,对系统的影响是永久的。

 

1.2、JAVA中的JDBC事务的实现

文章提到@Transactional注解,这个涉及到JAVA中的事务实现。Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。

这里应该主要涉及的是JDBC事务的实现,SPRING应该就是封装了一下。以下的文章对于JDBC进行数据库事务的操作讲解比较详细。

1)使用JDBC进行数据库的事务操作(1),这篇文章比较详细的阐述了一下用MYSQL的命令行怎么针对事务的失败、成功和回滚操作。

2)使用JDBC进行数据库的事务操作(2),这篇文章比较详细的阐述了用JDBC是如何实现事务的。讲了一个小附加点是可以支持事务回滚到指定位置再commit。

OK,其实这里解决我的一个核心问题的结论是:JDBC事务其实还是依赖数据库对事务的实现机制

再看其它文章中针对JDBC事务的讲解就好理解了,截取一些如下:

用 Connection 对象控制,JDBC 的 Connection 接口提供了两种事务模式:自动提交、手工提交。

Connection : java.sql.Connection

JDBC 为使用 Java 进行数据库的事务操作提供了最基本的支持。可以将多个 SQL 语句放到同一个事务中,保证其 ACID 特性。

优点: API 比较简单,可以实现最基本的事务操作,性能也相对较好。
缺点:事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。

代码实现示例:

public void JdbcTransfer() { 
    java.sql.Connection conn = null;
     try{ 
         String url = "jdbc:mysql://localhost:3306/test?user=root&password=root";  //定义连接数据库的url
         conn = DriverManager.getConnection(url); 
         // 将自动提交设置为 false,
         //若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交
         conn.setAutoCommit(false);
​
         // 获取一个执行、发送 SQL 语句的对象
         stmt = conn.createStatement(); 
         // 将 A 账户中的金额减少 500 
         stmt.execute("\
         update t_account set amount = amount - 500 where account_id = 'A'");
         // 将 B 账户中的金额增加 500 
         stmt.execute("\
         update t_account set amount = amount + 500 where account_id = 'B'");
​
         // 提交事务
         conn.commit();
         // 事务提交:转账的两步操作同时成功
     } catch(SQLException sqle){            
         try{ 
             // 发生异常,回滚在本事务中的操作
            conn.rollback();
             // 事务回滚:转账的两步操作完全撤销
             stmt.close(); 
             conn.close(); 
         }catch(Exception ignore){ 
            ignore.printStackTrace(); 
         } 
         sqle.printStackTrace(); 
     } 
}

 涉及到多数据库的操作或者分布式场景,JDBC事务就无能为力了,引出后续的分布式事务。

 

2、分布式事务

2.1、在网上各种找资料学习

这篇文章:讲解【分布式事务】的一篇良心之作!(需要对分布式系统有个认知可以看:到底啥是分布式系统开发经验?),总结一下,文中比较好的引出了分布式事务的产生原因,且提到了三种分布式事务的实现思路,分别是:

  1. 最终一致性实现方案
  2. 最大努力通知方案
  3. tcc强一致性方案

看的比较清晰明了,但是似乎没有我想找到X/Open XA的分布事务功能,所以继续找资料。

如果直接搜X/Open XA,发现好多的文章都是描述了一些X/Open XA的规范,看的人云里雾里。

这里找到了一篇知乎的文章:面试必问:分布式事务六种解决方案,看起来不那么抽象,有比较具体的抽丝剥茧过程,包括异常场景分析之类的,包括2PC、3PC、TCC、本地消息表、消息事务,最大努力通知等。描述了强一致性和最终一致性的方案,强一致性的最终极端代价似乎还是人工介入解决,最终一致性相对好些。

还有这篇文章不错:聊聊分布式应用的分布式事务之消息最终一致性事务,里面还有几个其它姊妹链接讲的也蛮好的。

  1. 聊聊分布式应用的分布式事务2PC/3PC(文中有对XA规范的描述)
  2. 聊聊分布式应用的分布式事务TCC
  3. 聊聊分布式应用的分布式事务之最大努力通知型事务
  4. 聊聊分布式应用的分布式事务之消息最终一致性事务

 

2.2、上一波概念

2.2.1、分布式事务

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上,通常一个分布式事务中会涉及对多个数据源或业务系统的操作。

还是这个场景举例:一个跨银行的转账操作涉及调用两个异地的银行服务,其中一个是本地银行提供的取款服务,另一个则是目标银行提供的存款服务,这两个服务本身是无状态并且相互独立的,共同构成了一个完整的分布式事务。如果从本地银行取款成功,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值