事务
概念:
- 作为单个逻辑工作单位执行的一系列操作,要么全部执行,要么全部不执行
事务的4个属性:
事务并发处理带来的问题
- 更新丢失:
- 两个或多个事务选择了同一行,最后更新覆盖了其他事务所做的更新操作
- 脏读:
- 事务A读到了事务B已经修改但是未曾提交的数据,并进行了修改等操作。如果事务B回滚,则A读到的数据无效
- 不可重复读:
- 在一个事务内读取表中的某一行数据,多次读取结果不同
- 幻读:
- 一个事务内读取到了别的事务插入的数据,导致前后读取到的数据不一致
分布式事务
举个栗子:
- 在某宝上购买东西,付钱之前的操作全部成功了,但是优惠券的折扣失效,那么此优惠券的服务即代表运行失败。那么某宝肯定是不赔的,用户肯定会不乐意。
- 那么如何才能保证在不同的服务下,保证该所有服务都成功呢?此时就出现了分布式事务
描述:
- 分布式环境下不同服务之间通过网络远程协作完成的事务
著名的分布式事务代表:
- 2pc(两段式提交)
- 3pc(三段式提交)
- TCC( Try、Confirm、Cancel)
- 半消息/最终一致性(RocketMQ)
2pc:
- 将整个事务流程分为两个阶段:准备阶段+提交阶段
- 准备阶段:执行事务代码,但不一定提交,
- 提交阶段:决定最终提交事务还是回滚事务
成功提交:
失败提交:
Seata的2PC和传统2PC比较
- 传统:
- RM就是数据库本身,第二阶段才提交
- Seata:
- RM以jar包的形式作为中间件部署在应用程序一侧,在第一阶段就会将本地事务提交,减少了Phase2的持锁,提高整体效率
2pc的问题:
-
2PC的事务协调者一定要高可用,即把Seata设置成集群(否则会出现单点故障问题)。
-
此外,当经历了阶段一协调者告诉参与者可以提交时,参与者1和2正确提交,参与者3因为网络原因没有收到消息则无法提交。此时会出现数据不一致的情况
3pc
描述:
- 在2pc的基础上增加了CanCommit阶段,并引入了超时机制。
- 一旦事务参与者迟迟没有收到协调者Commit请求,就会自动进行本地Commit,这样有效的解决了2pc的协调者单点故障的问题。但是性能问题和不一致问题没有得到根本解决。
三个阶段:
- 预备------即事务协调者会先循环参与者是否可以完成各自的请求,参与者需向协调者回复是或否
- 仍可理解为2pc的准备,事务协调者让参与者执行各自的请求,参与者执行完毕回复协调者
- 提交事务或者回滚事务。
TCC
描述:
- 要求每个分支事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。
- Try操作做业务检查及资源预留,
- Confirm做业务确认操作,
- Cancel实现一个与Try相反的操作即回滚操作。
- TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。
TCC的问题:
- 对应用程序的侵入性很强
- 2PC通常都是在跨库的DB层面,而TCC则在应用层面的处理,需要通过业务逻辑来实现。
优势和不足:
- 优势:可以让应用自己定义数据操作的粒度,使得降低锁冲突、提高吞吐量成为可能。
- 不足:对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。
使用场景
微服务之间远程调用完成事务操作
’
单体系统访问多个数据库实例
多服务访问同一个数据库实例