-
Notifications
You must be signed in to change notification settings - Fork 8.8k
Operate transactions survey
WangLiang/王良 edited this page Jan 9, 2024
·
5 revisions
随着分布式系统的广泛应用,分布式事务的需求也越来越多,场景也越来越复杂,针对不同的场景,也出现了各种各样的分布式事务解决方案,Seata也提供了四种事务模式:AT、TCC、XA、SAGA。
但是,随着复杂性的不断提升,开发分布式事务的难度也越来越大。在实际开发和应用过程中,经常会出现一些异常情况,或遗漏的地方,导致部分事务因异常而无法继续执行。从而导致系统中的部分数据没有达成一致性,以及部分数据被锁住的情况,最终导致系统局部功能不可用。
当系统中的分布式事务出现异常情况的时候,往往需要人工介入处理,然而,目前Seata的控制台并没有操作事务的功能。那么,就需要添加相应的功能了。
Seata存在四种模式:AT、TCC、XA、SAGA,和三种重要数据:全局事务数据、分支事务数据、全局锁数据,其中,全局事务数据和分支事务数据都存在非常多的状态。
针对不同的模式、不同的数据、不同的状态,都需要不同的操作。
- Begin:刚开启全局事务,分支事务可能正在执行或未执行
- Committing:正在提交(所有分支事务执行完后,TM向TC发起全局提交)
- CommitRetrying:提交分支事务返回的不是
PhaseTwo_Committed
跟PhaseTwo_CommitFailed_Unretryable
,或者分支事务提交出现异常,设置为该状态 - Rollbacking:正在回滚(TM发起全局回滚,TC接受后更改的状态)
- TimeoutRollbacking:定时任务检查出该全局事务超时后设置
- 某个分支事务回滚时出现异常或返回的分支状态不是
PhaseTwo_Rollbacked
或PhaseTwo_RollbackFailed_Unretryable
(比如返回的是XA分支重试状态或者PhaseTwo_RollbackFailed_Retryable
):会设置如下状态- TimeoutRollbackRetrying:回滚失败且全局事务超时(通常是被定时任务检查出的)
- RollbackRetrying:回滚失败但全局事务并没有超时
- AsyncCommitting:TC在提交时,如果可以异步提交便异步提交,仅仅针对AT模式
- Committed:最终状态,已经成功提交
- CommitFailed:最终状态,提交分支事务返回PhaseTwo_CommitFailed_Unretryable时会设置
- Rollbacked:最终状态,已经成功回滚
- TimeoutRollbacked:最终状态,所有分支事务已经成功回滚,不过后续发现全局事务超时
(
SessionStatusValidator.isTimeoutGlobalStatus()
进行判断) - RollbackFailed:最终状态,回滚分支事务时返回
PhaseTwo_RollbackFailed_Unretryable
或者定 时任务重试回滚时超时(超过 MAX_ROLLBACK_RETRY_TIMEOUT ) - TimeoutRollbackFailed:最终状态,回滚分支事务返回
PhaseTwo_RollbackFailed_Unretryable
且全局事务超时 - Finished:最终状态,当
GlobalSession
为空会进行标识(不一定会持久化),在SAGA
模式在分支 事务返回PhaseOne_Failed
会进行持久化 - CommitRetryTimeout:最终状态,定时任务重试提交超时,不会持久化,仅仅做通知
- RollbackRetryTimeout:最终状态,定时任务重试回滚超时,不会持久化,仅仅做通知
- Registered:注册分支
- PhaseOne_Done:仅仅AT模式,分支事务commit时设置(不一定会,根据配置)
- PhaseOne_Failed:分支事务执行失败,分支事务rollback时设置(肯定会)
- PhaseOne_Timeout:未使用
- PhaseTwo_Committed:TC向RM发送分支事务提交,提交成功后RM返回该状态(不会持久化,直接remove分支)
- PhaseTwo_CommitFailed_Retryable:TC向RM发送分支事务提交,提交失败后RM返回该状态(不同模式处理具体不同)(不会持久化,全局事务会持久化相应状态,见上)
- PhaseTwo_CommitFailed_Unretryable:TC向RM发送分支事务提交,分支事务提交出现异常返回该状态(不同模式处理具体不同)(不会持久化,全局事务会持久化相应状态,见上)
- PhaseTwo_Rollbacked:TC向RM发送分支事务回滚,回滚成功后RM返回该状态(不会持久化,直接remove分支)
- PhaseTwo_RollbackFailed_Retryable:TC向RM发送分支事务提交,提交失败后RM返回该状态(不同模式处理具体不同)(不会持久化,全局事务会持久化相应状态,见上)
- PhaseTwo_RollbackFailed_Unretryable:TC向RM发送分支事务提交,分支事务提交出现异常返回该状态(不同模式处理具体不同)(不会持久化,全局事务会持久化相应状态,见上)
- PhaseTwo_CommitFailed_XAER_NOTA_Retryable:针对XA提交失败重试(不会持久化,不过全局事务会设置为重试状态)
- PhaseTwo_RollbackFailed_XAER_NOTA_Retryable:针对XA回滚失败重试(不会持久化,不过全局事务会设置为重试状态)
-
删除全局事务操作:
- Committing,Rollbacking : 不允许删除,正在提交/回滚,删除的话需要删除对应分支纪录,但 本身状态已经正在删除了,在手动删除一次也没意义
- CommitRetrying,RollbackRetrying,TimeoutRollbacking,TimeoutRollbackRetrying : 删 除对应的全局锁,删除分支事务但不会去调用对应的分支回滚/提交去通知RM,交由用户手工提 交/回滚(出现异常的情况)
- AsyncCommitting: 允许删除,删除对应的全局锁,删除分支事务且会调用对应的分支回滚/提 交去通知RM
- Begin与其他最终状态: 不允许删除
-
停止提交或回滚重试操作:
-
继续提交 或 回滚重试 操作:
- 尝试回滚的状态条件: GlobalStatus.TimeoutRollbacking,GlobalStatus.TimeoutRollbackRetrying,GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking ,其中GlobalStatus.Rollbacking 需要判断 isDeadSession
- 尝试提交的状态条件: GlobalStatus.Committing, GlobalStatus.CommitRetrying ,其中GlobalStatus.Committing 需要判断 isDeadSession
- 做法: 增多一个old_status字段与一个GlobalStatus的停止状态,停止提交/回滚时将 old_status设置为一开始的status字段对应的状态,然后把status字段设置为停止状态,定时任务就不会查询到停止状态的全局事务,自然不会进行重试,继续提交/回滚时update回去即可
-
发起单次提交 / 回滚 操作:
- 单次提交操作: 条件:状态为CommitRetrying或AsyncCommitting 做法:调用doGlobalCommit
- 单次回滚操作: 条件: 状态为RollbackRetrying,TimeoutRollbacking,TimeoutRollbackRetrying 做法: 调用doGlobalRollback
-
变更状态操作:
- 可以将一些失败的状态改为重试的状态:比如CommitFailed或RollbackFailed改为CommitRetrying于RollbackRetrying
- 其他状态:不允许变更
-
修改超时时间操作: 进行Update的操作,修改字段timeout,因为有一个定时任务检查是否超时,因此可能存在误判。
解决方法: 在定时任务检查时更新条件加上timeout=xxx,如果修改失败则说明timeout被修改 了,避免出现误判的情况
-
大部分分支事务状态并没有持久化(包括也没在内存中改变),内存中会变化的都会持久化:
- AT:Registered,PhaseOne_Done和PhaseOne_Failed会持久化
- TCC:Registered持久化
- XA:Registered,PhaseOne_Failed才会持久化
- SAGA:Registered,PhaseOne_Failed,PhaseTwo_Committed,PhaseTwo_Rollbacked,PhaseTwo_RollbackFailed_Retryable,PhaseOne_Failed(Saga模式并不能删除分支事务,因为Saga需要按顺序回滚)
-
删除分支事务:
- PhaseOne_Done 不允许删除,因为可能二阶段需要回滚,删除可能会造成全局事务提交/回滚不一致
- 条件与做法:
- 条件:分支状态为 PhaseOne_Failed 删除分支事务; 做法:移除分支即可,不需要branchRollback回滚事务,因为没有执行成功
- 条件:其全局事务对应重试的状态(定时任务重试)允许删除,不需要调用branchRollback回滚(由用户手动处理)
-
暂时跳过失败重试:
-
继续重试:
- 条件:对应全局事务为对应重试状态
- 做法:同理全局事务做法,增多一个old_status字段,增多一个BranchStatus的停止状态,暂时设置old_status与status,定时任务在回滚全局事务时,遇到停止状态的分支事务过滤即可,特别的:针对Saga模式,也进行暂停全局事务的回滚
全局锁仅仅针对AT模式,释放锁,由于AT模式大部分分支状态并没有持久化,因此根据全局事务状态决定是否释放锁操作:
- 条件:全局事务状态为重试状态:CommitRetrying,TimeoutRollbacking,TimeoutRollbackRetrying,RollbackRetrying时允许删除,原因:其他状态的全局事务删除可能会导致脏写或没必要删除,这些状态的事务删除也会有脏写问题,但提前删除保证不会因为一直重试而不释放
- 做法:删除对应的全局锁(对应DB一行纪录,LockManager方法即可)
针对以下操作,前端需要给出相应的警告提示:
- 释放全局锁:提示用户可能出现脏写
- 删除全局事务(状态为CommitRetrying,RollbackRetrying,TimeoutRollbacking,TimeoutRollbackRetrying时):提示用户需要手工提交/回滚,可能出现全局事务提交/回滚不一致(因为删除后不会不在回滚/提交且删除时不会去尝试回滚/提交所有分支事务)
- 删除分支事务(全局事务对应重试的状态):提示用户需要手工提交/回滚,可能出现全局事务提交/回滚不一致(因为删除后不会不在回滚/提交且删除时不会去尝试回滚/提交该分支事务)