⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://www.jianshu.com/p/4cb127b737cf 「阳雨人」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

Fescar全局锁的理解

前几天夜里,我老大发我一篇文章说阿里的GTS开源了.
因为一直对分布式事务比较感兴趣。立马pull了代码,进行阅读。
基本的原理,实现方案我就不一一细化了,详细见官方文档(写的很棒,点赞)。

在fescar的社区,大家比较关注的是通过fescar回滚到before快照前,别的线程假如更新了数据,且业务走完了,那么恢复的这个快照不就是脏数据了么。
很显然,这种情况在fescar中是不被允许的。

那么fescar是如何做的呢?

我们先简单了解一下fescar的设计原理

那些一上来就喜欢看源码的同学,一定不要错过这么官方的图文介绍,看完再读源码事半功倍.

Fescar官方介绍

了解完Fescar的基本原理,我们重点关注下Fescar的全局排他锁

Fescar设计了一个**全局的排他锁**,来保证事务间的 **写隔离**。

关于隔离性:(这是Fescar官方给的一段话)

全局事务的隔离性是建立在分支事务的本地隔离级别基础之上的。

在数据库本地隔离级别 **读已提交或以上** 的前提下,Fescar 设计了由事务协调器维护的 全局写排他锁,来保证事务间的 写隔离,将 **全局事务默认定义在 读未提交 的隔离级别上**。

我们对隔离级别的共识是:绝大部分应用在读已提交的隔离级别下工作是没有问题的。而实际上,这当中又有绝大多数的应用场景,实际上工作在读未提交的隔离级别下同样没有问题。

在极端场景下,应用如果需要达到全局的读已提交,Fescar也提供了相应的机制来达到目的。
默认,Fescar 是工作在 读无提交 的隔离级别下,保证绝大多数场景的高效性。

我的解读

本地事务【读已提交】,fescar全局事务【读未提交】。这是这段话的核心。
我理解的这段话中fescar全局事务读未提交,并不是说本地事务的db数据没有正常提交,而是指全局事务二阶段commit|rollback未真正处理完(即未释放全局锁)。

总结来说:全局未提交但是本地已提交的数据,对其他全局事务是可见的【当然在本地事务提交后,本地事务提交前,隔离级别是本地事务的管辖范围】

for example
产品份额有5W,A用户购买了2万,份额branch一阶段完毕(本地事务份额已经扣除commit),但是在下单的时候异常了。
因为本地事务读已提交,这时候fescar允许业务访问该条数据,3W,在A用户的份额branch未回滚成功前,对其他用户可见。
但是其他用户并不能买该产品,必须等到产品份额回滚到5万,其他用户才可以操作产品数据。

所以看了这个例子 真的有必要做到全局事务读已提交么?

我们先来看一下Fescar的全局锁的做法

Fescar一阶段

1. 本地(Branch)在向TC注册的时候,把本地事务需要修改的数据table+pks提交到server端申请锁,拿到全局锁后,才能提交本地事务
2. 全局锁的结构:resourceId + table + pks
3. 锁是存在server端 branchSession中

Fescar二阶段

一阶段本地事务提交,db的锁释放了(for update锁),但是全局锁继续保持,
直到二阶段决议(注意释放锁的顺序):
1. 提交:TC 释放锁,通知branch提交后 (rm端异步处理)
2. 回滚:TC 通知branch回滚后,释放锁(rm端同步处理 执行undo_log)

Fescar如何保障锁的高效?

大家自己先思考下,最后给大家仔细解读官方的demo,并分析fescar的性能问题

Fescar目前开源版本全局锁的实现

大家有兴趣自己阅读:com.alibaba.fescar.server.lock.DefaultLockManagerImpl

官方的图实在是做的太漂亮了,clone一份解读 TC TM RM 以及全局锁的获取和释放动作发生点

分支事务如何工作?关注全局锁的获取和释放 特别是二阶段commit和rollback全局锁释放的顺序

img

ATBranch.png

Fescar中 RM TM TC如何工作的?

img

AT distribute transaction.png

看了这两张图,大家应该对fescar是如何工作的应该有一个大致的了解了。☺
1、全局锁的获取
2、tm tc rm之间如何通信工作
3、隔离级别问题的思考

最后我们来解读一遍官方的demo

  • branch1:update storage_tbl set count = count - ? where commodity_code = ?
  • branch2:update account_tbl set money = money - ? where user_id = ?
  • branch3:insert into order_tbl (user_id, commodity_code, count, money) values (?, ?, ?, ?)
  1. 线程A:执行branch1(pk:55),执行branch2的时候发现没钱了,扔了一个异常,那么势必需要回滚branch1的份额。
    • TM通知TC开始回滚branch1份额中
  2. 线程B:执行branch1(pk:55)
    • 如果线程A中branch1(pk:55)已经回滚成功了,那么B线程可以正常拿到锁走下去
    • 如果线程A中branch1还未回滚(resourceId+table+pk锁未释放)。当线程B发起branch1向server发起申请锁,会直接失败。

Fescar全局锁简单总结:操作一条记录的分支事务,必须等待这条记录的前一个分支事务执行结束(具体commit rollback情况分析如下),才能持有锁。

其实相比XA的锁,fescar在每个分支事务的一阶段结束后都释放了db的锁,所以fescar的性能瓶颈应该在于二阶段的执行速度(释放锁的快慢)

因为分布式事务在执行事务编排前,一般会校验业务的正确性,所以发生回滚的概率相对较低,所以先考虑二阶段commit操作。

  1. Commit场景分析:

    TM通知server进行commit,server立马释 branch的锁,然后再逐个通知RM提交
    消耗:1 rpc操作,(branch删除undo_log放在异步队列里面做)

  2. Rollback场景分析:

    TM通知server进行rollback,server通知RM回滚后立马释放 branch的锁。
    消耗:1 + N的rpc操作 + N的回滚sql操作

所以总的来看fescar在commit的释放全局锁还是非常高效的。

思考

1. server支持多台机器部署,应该如何改造?

全局锁的问题,锁改造;
全局事务向server0申请的,Branch1发到server1,branch2发到server2的问题,多机器恢复的情况,TC的改造

2. 全局锁在Fescar中更新确实是没有问题的,但是如果就是业务方需要手动调整DB数据呢?

大胆猜测,依赖Fescar写了一个管理平台 用来执行sql的。哈哈

3. 隔离级别的思考
Fescar默认工作在,本地事务读已提交,全局事务读未提交。
是否存在全局事务必须工作在【读已提交】级别而不能工作在【读未提交】的业务场景呢?
大家大胆脑洞 这个问题值得探讨。

4. Fescar的文档中说,是支持全局事务读已提交的,那么fescar是如何实现的呢?

感兴趣的同学可以试着读一下com.alibaba.fescar.rm.datasource.exec.SelectForUpdateExecutor

源码核心类

大家想读源码的话,可以重点关注一下几个类。有问题一起探讨。

TM相关
com.alibaba.fescar.tm.api.TransactionalTemplate

RM相关
com.alibaba.fescar.rm.datasource.exec.SelectForUpdateExecutor
com.alibaba.fescar.rm.datasource.ConnectionProxy
com.alibaba.fescar.rm.datasource.exec.AbstractDMLBaseExecutor
com.alibaba.fescar.rm.RMHandlerAT

TC相关
com.alibaba.fescar.server.coordinator.DefaultCoordinator
com.alibaba.fescar.server.coordinator.DefaultCore
com.alibaba.fescar.server.lock.DefaultLockManagerImpl

  • Github:https://github.com/alibaba/fescar
  • Fescar官方介绍:https://github.com/alibaba/fescar/wiki/Home_Chinese
文章目录
  1. 1. Fescar全局锁的理解
    1. 1.0.1. 我们先简单了解一下fescar的设计原理
    2. 1.0.2. 了解完Fescar的基本原理,我们重点关注下Fescar的全局排他锁
      1. 1.0.2.1. 关于隔离性:(这是Fescar官方给的一段话)
      2. 1.0.2.2. 我的解读
    3. 1.0.3. 我们先来看一下Fescar的全局锁的做法
    4. 1.0.4. Fescar一阶段
    5. 1.0.5. Fescar二阶段
    6. 1.0.6. Fescar如何保障锁的高效?
    7. 1.0.7. Fescar目前开源版本全局锁的实现
    8. 1.0.8. 官方的图实在是做的太漂亮了,clone一份解读 TC TM RM 以及全局锁的获取和释放动作发生点
    9. 1.0.9. 最后我们来解读一遍官方的demo
    10. 1.0.10. Fescar全局锁简单总结:操作一条记录的分支事务,必须等待这条记录的前一个分支事务执行结束(具体commit rollback情况分析如下),才能持有锁。
      1. 1.0.10.1. 其实相比XA的锁,fescar在每个分支事务的一阶段结束后都释放了db的锁,所以fescar的性能瓶颈应该在于二阶段的执行速度(释放锁的快慢)
      2. 1.0.10.2. 因为分布式事务在执行事务编排前,一般会校验业务的正确性,所以发生回滚的概率相对较低,所以先考虑二阶段commit操作。
      3. 1.0.10.3. 所以总的来看fescar在commit的释放全局锁还是非常高效的。
  2. 1.1. 思考
  3. 1.2. 源码核心类