Seata AT 模式执行流程
-
启动全局事务
- 业务系统调用
@GlobalTransactional注解(或显式调用 API),通知事务协调器(TC)启动一个全局事务。 - TC 分配一个唯一的全局事务 ID,并通知所有分支事务。
- 业务系统调用
-
记录 Before Image
- 在执行业务 SQL 之前,Seata 会代理拦截 SQL 语句,并记录该数据在执行前的快照(称为 Before Image)。
- Before Image 保存了 SQL 操作前的数据状态,目的是为了在出现问题时,能够回滚到该状态。
-
执行业务 SQL
- 系统执行业务逻辑中的 SQL 语句,完成相应的数据库操作。此时,本地事务仍未提交。
-
记录 After Image
- 在 SQL 执行后,Seata 会记录数据操作后的快照(称为 After Image)。
- After Image 保存了 SQL 操作后的数据状态,与 Before Image 共同用于事务补偿。
-
提交全局事务
- 当所有的本地事务成功执行,事务协调器(TC)会向所有分支事务发送提交(Commit)请求。
- 各分支事务接到提交请求后,提交本地事务,完成全局事务的提交。
-
回滚全局事务
- 如果在事务执行过程中,某个分支事务发生异常,事务协调器会发出全局回滚指令。
- 各分支事务根据 Before Image 生成相应的回滚 SQL,将数据回滚到执行事务之前的状态,确保数据一致性。
这样,Seata 的 AT 模式通过自动生成并执行补偿 SQL,实现了分布式事务的自动提交和回滚。
如何避免数据不一致
在 Seata 的 AT 模式下,本地事务的修改是在执行全局事务时先行生效的。为了避免在全局事务提交前,其他程序对数据库的修改,Seata 使用了**行锁(行级别锁定机制)**来确保数据的隔离性和一致性。具体通过以下几个机制来防止数据冲突:
1. 基于行级锁的保护
- 在执行业务 SQL 时,Seata 会对涉及的数据行进行行锁保护。
- 通过代理数据库操作,Seata 会在执行 SQL 时锁定相应的数据行(依赖数据库的行锁机制,例如 MySQL 的
for update),以防止其他事务对这些数据进行并发修改。 - 这些行锁会持续到全局事务提交或者回滚,确保同一行的数据在事务完成之前不会被其他事务修改。
2. 全局锁
- Seata 在数据库中引入了“全局锁”的概念,用于标记当前全局事务正在处理的资源。这个锁的粒度可以达到行级别,确保相同资源不能被多个事务并发修改。
- 在本地事务提交前,Seata 会在 TC 中登记这些全局锁。TC 会在全局事务提交或回滚完成后,释放这些锁。
3. 乐观锁(Optional)
- 虽然行锁能保证较高的隔离性,但在某些场景下,如果是长事务或者高并发操作,会对系统性能造成较大影响。Seata 也支持采用乐观锁机制,通过比较数据版本号(或时间戳)来避免并发冲突。
- 当事务提交时,Seata 会检查数据的版本号是否与事务开始时一致,如果一致,则可以提交。如果数据被其他事务修改,版本号不同,则回滚当前事务,确保不会产生数据不一致问题。
4. 脏写保护
- 当一个事务在修改数据后,还未完成全局事务提交,其他事务通过 Seata 代理访问时,如果发现数据已经被当前事务锁定,其他事务会进入等待状态,直到全局事务提交或回滚。
- 这种机制确保了脏写的避免,保证没有其他事务能修改尚未被提交的本地事务所涉及的数据。
5. 超时与死锁处理
- 为了避免由于锁竞争引起的死锁或长时间等待,Seata 提供了全局事务超时控制。如果全局事务在某一时间段内未能提交或者回滚,Seata 会强制回滚该事务,释放锁定的资源。
- 如果有死锁的情况出现,Seata 也能够自动识别,并中止一些冲突事务以确保系统能继续执行。
通过这些机制,Seata 的 AT 模式可以确保在全局事务完成前,其他程序无法修改该事务已经修改的数据,避免了数据不一致问题。