版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39800144/article/details/102730415
官方的文章中,有这么一段话:
全局事务的隔离性是建立在分支事务的本地隔离级别基础之上的。 在数据库本地隔离级别 读已提交 或以上的前提下,Fescar 设计了由事务协调器维护的 全局写排他锁,来保证事务间的 写隔离,将全局事务默认定义在 读未提交 的隔离级别上。 我们对隔离级别的共识是:微服务场景产生的分布式事务,绝大部分应用在 读已提交 的隔离级别下工作是没有问题的。而实际上,这当中又有绝大多数的应用场景,实际上工作在 读未提交 的隔离级别下同样没有问题。 在极端场景下,应用如果需要达到全局的 读已提交,Fescar 也提供了相应的机制来达到目的。默认,Fescar 是工作在 读未提交 的隔离级别下,保证绝大多数场景的高效性。
看一下下方这个官方场景案例: 1.有一张表:
2.有两个全局事务分别是tx1,tx2;
3.tx1和tx2都要执行这句sql:UPDATE a SET m = m -10 WHERE id = 1;
按照时间顺序,tx1和tx2执行了如下操作: tx1的执行流程如下:
UPDATE a SET m = m -10 WHERE id = 1;
但是还未commit;tx2的执行流程如下:
UPDATE a SET m = m -10 WHERE id = 1;
但是还未commit;tx2为了要local commit,tx2开始去获取global lock; 我们假设,t1 = T1,说明这个时间节点,两个事务有冲突,此时,全局锁被tx1持有,但是tx2想要local commit前需要先拿到全局锁,幸好,tx1事务进行顺利,在t2释放了全局锁,此时,tx2顺利拿到了全局锁,然后:
我们还是用刚才的案例,只是tx1执行过程中,由于tx1这个全局事务中,有其他业务执行失败了,此时决议全局回滚,那么,tx1需要重新获取该数据的本地锁,根据1阶段的回滚日志进行补偿操作,实现a表操作分支的回滚。
那么tx1的执行就成了:
tx1的执行流程如下:
UPDATE a SET m = m -10 WHERE id = 1;
但是还未commit;此时,tx1全局事务上有其他分支事务执行失败,tx1二阶段决议全局回滚。那么,tx1这里,要回滚a表的操作,是要先拿到local lock的;
而tx2的执行流程为: tx2的执行流程如下:
UPDATE a SET m = m -10 WHERE id = 1;
但是还未commit;如果很不巧,t1 = T1,也就是说,在这个尴尬的瞬间: tx1想要获取这条数据的本地锁,但是本地锁现在被tx2持有; tx2想要获取这条数据的全局锁,但是全局锁现在被tx1持有。
场景如下:
这时候会怎样呢?
tx1由于拿不到本地锁,会回滚失败,然后不断的进行重试,而tx2获取全局锁,是有超时时间限制的,一旦获取全局锁超时,tx2会放弃全局锁并回滚本地事务,然后释放本地锁,此时tx1拿到了本地锁,然后回滚成功。
在这个整个过程中,这条数据的全局锁,始终被tx1持有,所以是不会出现脏写的。