Skip to content

Operate transactions survey

WangLiang/王良 edited this page Jan 9, 2024 · 5 revisions

1. 分布式事务现状

随着分布式系统的广泛应用,分布式事务的需求也越来越多,场景也越来越复杂,针对不同的场景,也出现了各种各样的分布式事务解决方案,Seata也提供了四种事务模式:AT、TCC、XA、SAGA。

但是,随着复杂性的不断提升,开发分布式事务的难度也越来越大。在实际开发和应用过程中,经常会出现一些异常情况,或遗漏的地方,导致部分事务因异常而无法继续执行。从而导致系统中的部分数据没有达成一致性,以及部分数据被锁住的情况,最终导致系统局部功能不可用。

2. 人工介入处理分布式事务

当系统中的分布式事务出现异常情况的时候,往往需要人工介入处理,然而,目前Seata的控制台并没有操作事务的功能。那么,就需要添加相应的功能了。

3. Seata控制台操作事务的功能分析

Seata存在四种模式:AT、TCC、XA、SAGA,和三种重要数据:全局事务数据、分支事务数据、全局锁数据,其中,全局事务数据和分支事务数据都存在非常多的状态。

针对不同的模式、不同的数据、不同的状态,都需要不同的操作。

3.1 对数据状态进行梳理:

3.1.1) 全局事务状态梳理:

  • Begin:刚开启全局事务,分支事务可能正在执行或未执行
  • Committing:正在提交(所有分支事务执行完后,TM向TC发起全局提交)
  • CommitRetrying:提交分支事务返回的不是 PhaseTwo_CommittedPhaseTwo_CommitFailed_Unretryable,或者分支事务提交出现异常,设置为该状态
  • Rollbacking:正在回滚(TM发起全局回滚,TC接受后更改的状态)
  • TimeoutRollbacking:定时任务检查出该全局事务超时后设置
  • 某个分支事务回滚时出现异常或返回的分支状态不是 PhaseTwo_RollbackedPhaseTwo_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:最终状态,定时任务重试回滚超时,不会持久化,仅仅做通知

3.1.2) 分支事务状态梳理:

  • 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回滚失败重试(不会持久化,不过全局事务会设置为重试状态)

3.2. 对数据对象所需的操作进行梳理:

3.2.1)全局事务操作:

  • 删除全局事务操作:

    • 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被修改 了,避免出现误判的情况

3.2.2)分支事务操作分析:

  • 大部分分支事务状态并没有持久化(包括也没在内存中改变),内存中会变化的都会持久化:

    • 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模式,也进行暂停全局事务的回滚

3.2.3)全局锁操作分析:

全局锁仅仅针对AT模式,释放锁,由于AT模式大部分分支状态并没有持久化,因此根据全局事务状态决定是否释放锁操作:

  • 条件:全局事务状态为重试状态:CommitRetrying,TimeoutRollbacking,TimeoutRollbackRetrying,RollbackRetrying时允许删除,原因:其他状态的全局事务删除可能会导致脏写或没必要删除,这些状态的事务删除也会有脏写问题,但提前删除保证不会因为一直重试而不释放
  • 做法:删除对应的全局锁(对应DB一行纪录,LockManager方法即可)

3.3 操作警告设计:

针对以下操作,前端需要给出相应的警告提示:

  • 释放全局锁:提示用户可能出现脏写
  • 删除全局事务(状态为CommitRetrying,RollbackRetrying,TimeoutRollbacking,TimeoutRollbackRetrying时):提示用户需要手工提交/回滚,可能出现全局事务提交/回滚不一致(因为删除后不会不在回滚/提交且删除时不会去尝试回滚/提交所有分支事务)
  • 删除分支事务(全局事务对应重试的状态):提示用户需要手工提交/回滚,可能出现全局事务提交/回滚不一致(因为删除后不会不在回滚/提交且删除时不会去尝试回滚/提交该分支事务)
Clone this wiki locally