目录什么是事务?换个角度看事务Java 中的事务什么是分布式事务?分布式事务的几种实现思路总结前言在分布式、微服务大行其道的今天,相信大家对这些名词都不会陌生。而说到使用分布式,或者拆分微服务的好处,
目录
- 什么是事务?
- 换个角度看事务
- Java 中的事务
- 什么是分布式事务?
- 分布式事务的几种实现思路
- 总结
前言
在分布式、微服务大行其道的今天,相信大家对这些名词都不会陌生。而说到使用分布式,或者拆分微服务的好处,你肯定能想到一大堆。
比如每个人只需要维护自己单独的服务,没有了以前的各种代码冲突。自己想测试、想发布、想升级,只需要 care 自己写的代码就 OK 了,很方便很贴心!
然而事物都有两面性,但是它也同时也会带来的一些问题,今天的文章谈的就是分布式系统架构带来的其中一个棘手的问题:分布式事务
什么是事务?
首先抛出来一个问题:什么是事务?
有人会说事务就是一系列操作,要么同时成功,要么同时失败;然后会从事务的 ACID 特性(原子性、一致性、隔离性、持久性)展开叙述。 确实如此,事务就是为了保证一系列操作可以正常执行,它必须同时满足 ACID 特性。
但是今天我们换个角度思考下,我们不仅要知道 what(比如什么是事务),更要知道事务的 why(比如为什么会有事务这个概念?事务是为了解决什么问题)。
有时候,换个角度说不定有不一样的收获。
换个角度看事务
就像经典的文学作品均来自于生活,却又高于生活,事务的概念同样来自于生活,引入“事务”肯定是为了解决某种问题,不然,谁又愿意干这么无聊的事情呢?
最简单最经典的例子:银行转账,我们要从 A 账户转 1000 块到 B 账户。 正常情况下如果从 A 转出 1000 到 B 账户之后,A 账户余额减 1000(这个操作我们用 action1 代表),B 账户余额加 1000(这个操作我们用 action2 代表)
首先我们要明确一点,action1 和 action2 是两个操作。既然是两个操作那么就一定会存在执行的先后顺序。那么就可能会出现 action1 执行完刚准备去执行 action2 的时候出问题了(比如数据库负载过大暂时拒绝访问)。
类比到我们生活中,那就是我给朋友转了 1000 块钱,然后我卡里的余额少了 1000,但是我朋友确没有收到钱。
为解决这种“money 去哪儿了”的问题,引入了“事务”的概念。也就是说,既然我转账的时候你保证不了 100%能成功,比如银行系统只能保证 99.99%的高可用,那么在那 0.01%的时间里如果出现了上述问题,银行系统直接回滚 action1 操作?(即把 1000 块钱再加回余额中去) 对于银行系统来说,可能在 0.01%的时间里我保证不了 action1 和 action2 同时成功,那么在出问题的时候,我保证它俩同时失败。(事务的原子性) 通过这个例子,就已经回答了刚开始提出的 2 个问题(为什么会有事务?事务是为了解决什么问题?)
总结一下:事务就是通过它的 ACID 特性,保证一系列的操作在任何情况下都可以安全正确的执行。
Java 中的事务
搞清楚了事务之后,我们来看点眼熟的,java 中的事务是怎么玩的? Java 中我们平时用的最多的就是在 service 层的增删改方法上添加@Transactional 注解,让 spring 去帮我们管理事务。
它底层会给我们的 service 组件生成一个对应的 proxy 动态代理,这样所有对 service 组件的方法都由它对应的 proxy 来接管,当 proxy 在调用对应业务方法比如 add()时,proxy就会基于 AOP 的思想在调用真正的业务方法前执行 setAutoCommit(false)打开事务。
然后在业务方法执行完后执行 commit 提交事务,当在执行业务方法的过程中发生异常时就会执行 rollback 来回滚事务。
当然@Transactional 注解具体的实现细节这里不再展开,这个不是本篇文章的重点,本文的 topic 是“分布式事务”,关于@Transactional 注解大家有兴趣的话,可以自己打断点debug 源码研究下,源码出真知。
什么是分布式事务?
铺垫了辣么久,终于到了本篇的第一个重点!
首先大家想过没:既然有了事务,并且使用 spring 的@Transactional 注解来控制事务是如此的方便,那为啥还要搞一个分布式事务的概念出来啊? 更进一步,分布式事务和普通事务到底是啥关系?有什么区别?分布式事务又是为了解决什么问题出现的?
各种疑问接踵而至,别着急,带着这些思考,咱们接下来就详细聊聊分布式事务。
既然叫分布式事务,那么必然和分布式有点关系啦!简单来说,分布式事务指的就是分布式系统中的事务。
首先来看看下面的图:
.
.
比如跨银行转账的时候,要涉及到两个银行的分布式事务,如果用 TCC 方案来实现,思路是这样的:
- Try 阶段:先把两个银行账户中的资金给它冻结住就不让操作了;
- Confirm 阶段:执行实际的转账操作,A 银行账户的资金扣减,B 银行账户的资金增加;
- Cancel 阶段:如果任何一个银行的操作执行失败,那么就需要回滚进行补偿,就是比如 A 银行账户如果已经扣减了,但是 B 银行账户资金增加失败了,那么就得把 A 银行账户资金给加回去。
适用场景:这种方案说实话几乎很少有人使用,我们用的也比较少,但是也有使用的场景。
因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大,非常之恶心。
比如说我们,一般来说跟钱相关的,跟钱打交道的,支付、交易相关的场景,我们会用 TCC,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,在资金上不允许出现问题。
比较适合的场景:除非你是真的一致性要求太高,是你系统中核心之核心的场景,比如常见的就是资金类的场景,那你可以用 TCC 方案了。 你需要自己编写大量的业务逻辑,自己判断一个事务中的各个环节是否 ok,不 ok 就执行补偿/回滚代码。
而且最好是你的各个业务执行的时间都比较短。
但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护。
总结
本篇介绍了什么是分布式事务,然后还介绍了最常用的 3 种分布式事务方案。但除了上边的方案外,其实还有两阶段提交方案(XA 方案)和本地消息表等方案。但是说实话极少有公司使用这些方案,鉴于篇幅所限,不做介绍。
欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

- 0