From 44f2a8c54044e7ef94822b2545823ee954d77982 Mon Sep 17 00:00:00 2001 From: asahi Date: Tue, 26 Aug 2025 19:28:59 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E9=98=85=E8=AF=BBseata=20global=20exclu?= =?UTF-8?q?sive=20write=20lock=E7=9B=B8=E5=85=B3=E7=9A=84=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 分布式事务/Saga.md | 22 --------------- 分布式事务/TCC.md | 32 --------------------- 分布式事务/seata/seata.md | 51 ++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 54 deletions(-) delete mode 100644 分布式事务/Saga.md delete mode 100644 分布式事务/TCC.md create mode 100644 分布式事务/seata/seata.md diff --git a/分布式事务/Saga.md b/分布式事务/Saga.md deleted file mode 100644 index 01a05cf..0000000 --- a/分布式事务/Saga.md +++ /dev/null @@ -1,22 +0,0 @@ -# 分布式事务: Saga模式 -## Saga模型 -- 每个Saga都由一系列子事务Ti组成 -- 每个子事务Ti都有其对应的补偿动作Ci,补偿操作用于对Ti操作产生的结果进行撤销 -## Saga的执行顺序 -1. T1, T2, T3, ... ,Tn(全部执行成功) -2. T1, T2, T3, ..., Tj, Cj, Cj-1,... , C1 (执行出错之后回滚之前所有成功的子事务) -## 执行出错时,Saga的恢复策略 -1. 向后恢复(backword recovery),对所有已经完成的子事务进行补偿操作,任一子事务Ti执行失败,撤销掉之前所有执行成功的子事务,使得整个Saga的执行结果都撤销 -2. 向前恢复(forward recovery),重新尝试失败的事务。假设每个事务最终都会执行成功。其执行顺序为T1, ... Tj(失败), Tj(重试),...,...,Tn。该种情况下并不需要Ci操作对子事务Ti的操作结果进行撤销 -## Saga模式的结构 -Saga的嵌套结构只允许存在两个层次: -1. 顶层的Saga -2. 简单的子事务 -## Saga特性 -- 在外层(各个Sagas都在执行时,各个Sagas在执行过程中已经提交的子事务Ti,其执行结果对其他Sagas可见),隔离性并无法被满足,Sagas可能看到其他Sagas的执行结果 -- 每个子事务都是一个独立事务,各个子事务为独立的原子行为 -## Saga ACID -- 原子性:正常情况下可以保证 -- 一致性:执行过程中,可能会有A库和B库违反一致性要求的情况,但是最终成功之后是一致的 -- 隔离性:Sagas A能看到Sagas B部分执行的执行结果 -- 持久性:对于Sagas中的子事务Ti,其执行完成之后就会commit,而其撤销操作则是会调用Ci对已经提交的操作进行撤销操作 \ No newline at end of file diff --git a/分布式事务/TCC.md b/分布式事务/TCC.md deleted file mode 100644 index d797638..0000000 --- a/分布式事务/TCC.md +++ /dev/null @@ -1,32 +0,0 @@ -# 分布式事务: TCC模式 -## TCC事务 -TCC是Try、Confirm、Cancel三个单词的首字母缩写,TCC分为三个阶段:预处理(Try)、确认(Confirm)、撤销(Cancel)。 -## TCC工作流 -## 预处理阶段(Try phase) -在预处理阶段,请求者会请求服务的提供者做一些尝试性的操作。在该阶段,服务提供者会完成一些业务检查与验证,并且预占需要的业务资源。 -## 确认阶段(Confirm phase) -- 如果服务提供者成功执行了Try阶段,并且请求者决定继续执行操作,那么请求者可以在确认阶段执行确认操作。 -- ***具体的业务在确认阶段被执行***。 -- 在确认阶段,不会执行更多的验证操作,所有验证操作都会在Try阶段执行 -- 在Confirm阶段,只会使用Try阶段预留的业务资源 -> 通常,认为Confirm阶段是不会执行失败的,如果Try阶段被成功执行,那么Confirm阶段也能成功执行,如果Try成功而Confirm执行失败,会对Confirm阶段进行重试 -## 撤销阶段(Cancel phase) -在撤销阶段,如果Try阶段未正确完成且请求者决定不再继续执行,请求者可以在服务提供者上执行撤销操作。在Try阶段预占的业务资源应该在撤销阶段被正确释放 -***撤销阶段(Cancel)是对预处理阶段(Try)的反向操作*** -## TCC事务 -对于TCC事务,全局事务管理器首先会发起所有分支事务的Try操作 -- 若任何一个分支事务的Try操作执行失败,都会导致所有分支事务的Cancel操作被执行 -- 若所有分支事务的Try操作都执行成功,那么全局事务管理器会发起所有分支事务的Confirm操作 -在执行分支事务Confirm/Cancel操作时,如果执行失败,那么全局事务管理器会对失败操作进行重试 -> 通常,Cancel阶段也被认为是一定执行成功的,如果在Cancel执行时发生错误,需要进行重试或进行人工处理 -## TCC事务中需要注意的要点 -### 空回滚 -若是在没有调用TCC Try阶段的情况下直接调用二阶段的Cancel方法,Cancel方法需要识别出之前并未调用过Try阶段方法,直接返回Cancel成功 -### 幂等 -由于在各阶段执行时都存在重试机制,故而要保证Try、Confirm、Cancel接口都要保证幂等性,保证占用资源或释放资源的操作不会被重复执行 -### 悬挂 -在RPC调用过程中,由于网络拥堵等原因,Cancel操作可能先于Try操作执行。 -> 1. 如果先调用Try时,网络拥堵发生超时,那么TM会通知RM回滚分布式事务,调用Cancel操作 -> 2. 因为网络拥堵,可能调用完Cancel后,RPC的Try请求才到来 -> 3. 此时到来的Try请求会预留资源,而预留资源无法被其他事务所使用 - diff --git a/分布式事务/seata/seata.md b/分布式事务/seata/seata.md new file mode 100644 index 0000000..aca2201 --- /dev/null +++ b/分布式事务/seata/seata.md @@ -0,0 +1,51 @@ +# seata +seata作为兼具高性能和易用性的分布式事务解决方案,适用于微服务架构。 + +在使用seata的系统中,事务隔离级别如下: +- `branch transaction`/`local transaction`:在seata系统中,分支事务/本地事务的隔离级别为read committed +- `global transaction`:在seata系统中,全局事务的隔离级别为read uncommitted + - `global transaction`隔离级别为read uncommitted的含义如下:在全局事务尚未提交时,如果分支事务提交,则分支事务的修改对分布式系统外的其他事务可见 + +故而,在分布式系统中,若全局事务的隔离级别为read uncommitted,且没有其他机制用于修复该问题,其将会造成如下问题: +1 + +在上图所示中,全局事务A和全局事务B对相同的资源R进行了修改,并且分支事务A1和分支事务B1都本地提交,之后,全局事务A回滚,全局事务B提交,那么A无法将资源R回滚到一个合适的状态(例如,A1先对R进行修改,B1后对R进行修改,如果将R恢复到A1修改之前,那么B1对R的修改就会丢失)。 + +## Seata Global Exclusive Write Lock +`Seata Global Exclusive Write Lock`的代码实现位于`TC`(Transaction Coordinator) module中,RM(Resource Manager)module将会在`需要获取global lock`时对TC module进行请求,进而保证事务之间的写隔离。 + +故而,Seata Global Exclusive Write Lock会在TC module中被实现,并在RM module中被使用。 + +## TC - Global Exclusive Write Lock Implementation +在TC module中,RpcServer用于处理communication protocol related logic,而TC module实际的处理逻辑则由`DefaultCoordinator`进行处理。 + +> `DefaultCoordinator`中包含了TC module所有向外暴露的方法,例如 +> - doGlobalBegin: 创建全局事务 +> - doGlobalCommit: 提交全局事务 +> - doGlobalRollback: 全局事务回滚 +> - doBranchReport: branch transaction状态report +> - doBranchRegister:branch transaction注册 +> - 在AT中,branch register会在local transaction提交时进行,执行brach register时会实际占用global exclusive write lock +> - doLockCheck:global exclusive write lock校验 +> +> 实际上,上述方法都会被委托给`DefaultCore`来执行 + +在TC端的实现中,所有`获取锁`和`校验锁`的请求都最终会被`LockManger`执行,所有关于global exclusive write lock的设计都被维护在DefaultLockManagerImpl中。 + +## RM - Global Exclusive Write Lock Usage +在RM module中,主要会使用两个global write lock相关的方法: +- 校验是否global lock可以被获取 +- 注册transaction branch,并实际获取global lock + +global write lock的释放和RM无关,当全局事务提交时,全局事务相关的global lock将会自动被TC module释放。当注册分支事务之前,会检查global lock状态,确定在branch register的过程中不会发生锁冲突。 + +当执行update、insert、delete语句时,在执行前后都会以undo log的形式生成data snapshot,并且,生成快照的形式会基于`select ... for update`。 + +在本地事务执行提交操作时,会执行branch register操作,该操作中会向TC module发送请求,要求占用global write lock。 + +故而,通过global write lock机制,seata能够保证全局事务未提交前,分支事务占用的锁资源并不会被释放,全局事务外的其他事务想要访问数据时,首先需要校验是否global exclusive write lock已经被占用,上述流程能够保证全局事务能够正确的回滚。 + +但是,在全局事务提交前,其他事务必须等待global exclusive write lock的释放,这种等待会带来性能损耗。 + + +