主頁 > 軟體設計 > 分布式事務(Seata)

分布式事務(Seata)

2022-07-21 10:19:00 軟體設計

前言

在分布式的微服務架構中,鑒于服務單一職責性,各個微服務都分布在不同的服務器節點,且每1個微服務是獨立的;

在后端每個微服務都是分散和獨立的,可能使用不同編程語言,使用不同的資料庫,通過RPC呼叫完成前端用戶發送的請求(任務);

假設1個用戶在1個分布式微服務架構的電商網站購物,購買了1件商品點擊了下單,后臺需要一組微服務協作完成下單操作;

即:訂單服務創建訂單--->庫存服務減少庫存---->用戶服務減少余額;

在這3個環節執行程序中,如果其中1個微服務不可用,都無法滿足當前用戶下單的需求; 

那如何保證用戶的1次操作具備資料庫事務的ACID特性呢?

這就需要分布式事務;

 

 一、CAP理論

2000年加州理工大學伯克利分校的EricBrewer教授提出在分布式架構中應當具備3個理想的指標

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance (磁區容錯性)

稱為CAP理論;

1.一致性

指分布式架構中資料的強一致性, 即使資料分布在不同節點上,1個節點寫操作之后,在另1個節點的讀操作,也可以讀到最新狀態的資料(MySQL主從之間資料同步),

2.可用性

指服務處在100%可用的狀態,用戶訪問分布式集群中任意1個服務 ,即使該服務故障,用戶依然能得到回應,而不是超時或拒絕

3.磁區容錯

指分布式架構中物理節點, 即使遇到網路故障或其它原因導致分布式系統中的部分節點與其它節點失去連接無法通信,故障節點可以形成獨立磁區,不影響全域,

 

二、BASE理論

在分布式微服務架構中,各個微服務之間通過網路進行RPC呼叫,CAP理論中的可用性和一致性就是相互矛盾的,無法同時成立;

1.CAP理論的問題

在分布式微架構中,如果想要實作一致性就需要在2個節點之間通過網路同步資料;

假設G1和G2兩個節點要想實作資料一致性,就需要通過網路傳輸同步資料,但網路傳輸資料是需要時間的

忠和孝無法兩全,如果G1更新了資料,在G1和G2節點通過網路傳輸同步資料的這1段時間里,G2節點是對外提供查詢服務呢?還是不提供服務呢?

  • G2提供服務就不滿足一致性指標;
  • G2阻塞用戶請求,不提供服務就無法滿足100%可用性指標;

以上是CAP理論存在的問題;

2.BASE理論

BASE理論是針對CAP理論存問題的1種解決方案,包含3個思想:

2.1.Basically Available (基本可用)

分布式系統在出現故障時,允許損失部分可用性,即保證核心可用,

2.2.Soft State(軟狀態)

在一定時間內,允許出現中間狀態,比如臨時的不一致狀態,

2.3.Eventually Consistent(最終一致性)

雖然無法保證強一致性,但是在軟狀態結束后,最終達到資料一致,

 

三、分布式事務解決思路

分布式事務在設計之初借鑒CAP、BASE理論,有兩種解決思路:

  • AP模式:各子事務分別執行和提交,允許出現結果不一致,然后采用彌補措施恢復資料即可,實作最終一致,

  • CP模式:各個子事務執行后互相等待,同時提交,同時回滾,達成強一致,但事務等待程序中,處于弱可用狀態,

但不管是哪一種模式,想要在分布式架構中控制多個事物都需要1個事務協調者(TC)

以下下單、扣款、商品出庫環節稱為分支事務,整個環節稱為全域事務

四、初識Seata

Seata是 2019 年1 月份螞蟻金服和阿里巴巴共同開源的分布式事務解決方案,

致力于提供高性能和簡單易用的分布式事務服務,為用戶打造一站式的分布式事務解決方案,

官網地址:http://seata.io/,其中的檔案、播客中提供了大量的使用說明、原始碼分析,

1.Seata的架構

Seata事務管理中有3個重要的角色:

  • TC (Transaction Coordinator) - 事務協調者:搭建好的Seata服務器,所有的RM和TM都要想TC上報,當前分支事務執行的狀態,以便于TC作出全域事務的回滾/提交操作(CEO);

  • TM (Transaction Manager) - 事務管理器:代表整個業務邏輯,決定全域事務的范圍和邊界,從哪個環節開始全域事務?在哪個環節提交或回滾全域事務?從哪個環節結束全域事務?(經理)

  • RM (Resource Manager) - 資源管理器:具體執行分支事務的微服務,(員工)

 

Seata基于上述架構提供了四種不同的分布式事務解決方案:

  • XA模式:強一致性分階段事務模式,犧牲了一定的可用性,無業務侵入

  • TCC模式:最終一致的分階段事務模式,有業務侵入

  • AT模式:最終一致的分階段事務模式,無業務侵入,也是Seata的默認模式

  • SAGA模式:長事務模式,有業務侵入

無論采用哪1種分布式事務解決方案,都需要先搭建Seata服務器,也就是事務的協調者(TC),

 

2.安裝Seata

2.1.Seata組態檔

使Seata服務注冊到Nacos中,并指定讀取Nacos中的組態檔;

registry {
  # tc服務的注冊中心類,這里選擇nacos,也可以是eureka、zookeeper等
  type = "nacos"

  nacos {
    # seata tc 服務注冊到 nacos的服務名稱,可以自定義
    application = "seata-tc-server"
    serverAddr = "127.0.0.1:8848"
    group = "DEFAULT_GROUP"
    namespace = ""
    cluster = "SH"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # 讀取tc服務端的組態檔的方式,這里是從nacos配置中心讀取,這樣如果tc是集群,可以共享配置
  type = "nacos"
  # 配置nacos地址等資訊
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "DEFAULT_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}
registry.conf

2.2.nacos添加配置

特別注意,為了讓tc服務的集群可以共享配置,我們選擇了nacos作為統一配置中心,

因此服務端組態檔seataServer.properties檔案需要在nacos中配好,

配置內容如下

registry {
  # tc服務的注冊中心類,這里選擇nacos,也可以是eureka、zookeeper等
  type = "nacos"

  nacos {
    # seata tc 服務注冊到 nacos的服務名稱,可以自定義
    application = "seata-tc-server"
    serverAddr = "127.0.0.1:8848"
    group = "DEFAULT_GROUP"
    namespace = ""
    cluster = "SH"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # 讀取tc服務端的組態檔的方式,這里是從nacos配置中心讀取,這樣如果tc是集群,可以共享配置
  type = "nacos"
  # 配置nacos地址等資訊
  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "DEFAULT_GROUP"
    username = "nacos"
    password = "nacos"
    dataId = "seataServer.properties"
  }
}
seataServer.properties

2.3.創建資料庫表

TC服務在管理分布式事務時,需要記錄事務相關資料到資料庫中,你需要提前創建好這些表,

新建一個名為seata的資料庫,seata資料庫中創建branch_table和global_table兩張表;

 這些表主要記錄全域事務、分支事務、全域鎖資訊:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- 分支事務表
-- ----------------------------
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table`  (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `status` tinyint(4) NULL DEFAULT NULL,
  `client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime(6) NULL DEFAULT NULL,
  `gmt_modified` datetime(6) NULL DEFAULT NULL,
  PRIMARY KEY (`branch_id`) USING BTREE,
  INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- 全域事務表
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table`  (
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `timeout` int(11) NULL DEFAULT NULL,
  `begin_time` bigint(20) NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`xid`) USING BTREE,
  INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
  INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;
table.sql

2.4.啟動TC服務

進入bin目錄,運行其中的seata-server.bat即可

2.5.測驗

打開瀏覽器,訪問nacos地址:http://localhost:8848,然后進入服務串列頁面,可以看到seata-tc-server的資訊:

 

五、微服務集成Seata

此時Seata已經注冊到Nacos并可以從Nacos中讀取配置資訊;

我們就可以在微服務中集成使用Seata服務了;

1.引入依賴

<!--seata-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <!--版本較低,1.3.0,因此排除--> 
        <exclusion>
            <artifactId>seata-spring-boot-starter</artifactId>
            <groupId>io.seata</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <!--seata starter 采用1.4.2版本-->
    <version>${seata.version}</version>
</dependency>

2.配置TC地址

微服務通過application.yml中配置的Nacos獲取TC地址;

seata:
  registry: # TC服務注冊中心的配置,微服務根據這些資訊去注冊中心獲取tc服務地址
    type: nacos # 注冊中心型別 nacos
    nacos:
      server-addr: 127.0.0.1:8848 # nacos地址
      namespace: "" # namespace,默認為空
      group: DEFAULT_GROUP # 分組,默認是DEFAULT_GROUP
      application: seata-tc-server # seata服務名稱
      username: nacos
      password: nacos
  tx-service-group: seata-demo # 事務組名稱
  service:
    vgroup-mapping: # 事務組與cluster的映射關系
      seata-demo: SH

 

六、Seata的事務模式

Seata提供4種事務模式,幫我們實作分布式事務;

分布式事務都是基于兩階段提交原理的;

 

1.兩階段提交

XA是規范,目前主流資料庫都實作了這種規范,實作的原理都是基于兩階段提交,

正常情況:

 例外情況:

一階段(事務預提交):

  • 事務協調者通知每個事物參與者,去資料庫執行事務的預提交;

  • 事務參與者預提交完成之后,向事務協調者報告事務預提交狀態;(此時事務不是真正的提交,繼續持有資料庫鎖)

二階段:

  • 事務協調者基于一階段的報告來判斷下一步操作

    • 如果一階段的分支事務全部預提交成功,則通知所有事務參與者去資料庫正真提交事務

    • 如果一階1個事務參與者的預提交結果失敗,則通知所有事務參與者回滾事務 

 

2.XA模式

幾乎所有主流的資料庫都XA 規范提供了支持;

Seata的XA模式是利用資料庫底層事務二階段提交的特性,封裝了二階段提交操作方法;

2.2.Seata的XA模型

Seata對原始的XA模式做了簡單的封裝和改造,以適應自己的事務模型,基本架構如圖:

RM一階段的作業:

① 注冊分支事務到TC

② 執行分支業務sql但不提交

③ 報告執行狀態到TC

TC二階段的作業:

  • TC檢測各分支事務執行狀態

    a.如果都成功,通知所有RM提交事務

    b.如果有失敗,通知所有RM回滾事務

RM二階段的作業:

  • 接收TC指令,提交或回滾事務

2.3.優缺點

XA模式的優點是什么?

  • 事務的強一致性,滿足ACID原則,

  • 常用資料庫都支持,實作簡單,并且沒有代碼侵入

XA模式的缺點是什么?

  • 因為一階段需要鎖定資料庫資源,等待二階段結束才釋放,性能較差

  • 依賴關系型資料庫實作事務

2.4.實作XA模式

Seata的starter已經完成了XA模式的自動裝配,實作非常簡單,步驟如下:

2.4.1.修改application.yml檔案

每個參與事務的微服務都需要開啟XA模式:

seata:
  data-source-proxy-mode: XA

2.4.2.添加注解

給發起全域事務的入口方法添加@GlobalTransactional注解

    @Override
    @Transactional
    //給發起全域事務的入口方法添加@GlobalTransactional注解:
    @GlobalTransactional
    public Long create(Order order) {
        // 創建訂單
        orderMapper.insert(order);
        try {
            // 扣用戶余額
            accountClient.deduct(order.getUserId(), order.getMoney());
            // 扣庫存
            storageClient.deduct(order.getCommodityCode(), order.getCount());

        } catch (FeignException e) {
            log.error("下單失敗,原因:{}", e.contentUTF8(), e);
            throw new RuntimeException(e.contentUTF8(), e);
        }
        return order.getId();
    }

2.4.3.重啟服務并測驗

重啟order-service,再次測驗,發現無論怎樣,3個微服務都能成功回滾,

3.AT模式

AT模式同樣是分階段提交的事務模型,不過缺彌補了XA模型中資源鎖定周期過長的缺陷

AT模式就是模仿了資料庫底層XA二階段提交機制,把改功能抽離出來在Seata中再實作,這樣可以使Seata支持更多資料庫的分布式事務操作;

階段一RM的作業:

  • 注冊分支事務

  • 記錄undo-log(資料快照以保證事務提交失敗之后可以回滾)

  • 執行業務sql并提交

  • 報告事務狀態

階段二提交時RM的作業:

  • 洗掉undo-log即可

階段二回滾時RM的作業:

  • 根據undo-log恢復資料到更新前 

3.1.AT與XA的區別

簡述AT模式與XA模式最大的區別是什么?

  • XA模式一階段不提交事務,鎖定資源;AT模式一階段直接提交,不鎖定資源,

  • XA模式依賴資料庫機制實作回滾;AT模式利用資料快照實作資料回滾,

  • XA模式強一致;AT模式最終一致

3.2.臟寫問題

在多執行緒并發訪問AT模式的分布式事務時,有可能出現臟寫問題,如圖:

 

 解決臟寫問題的思路就是引入了全域鎖的概念,

在釋放DB鎖之前,先拿到全域鎖,避免同一時刻有另外一個事務來操作當前資料,

3.3.優缺點

AT模式的優點:

  • 一階段完成直接提交事務,釋放資料庫資源,性能比較好

  • 利用全域鎖實作讀寫隔離

  • 沒有代碼侵入,框架自動完成回滾和提交

AT模式的缺點:

  • 兩階段之間屬于軟狀態,屬于最終一致

  • 框架的快照功能會影響性能,但比XA模式要好很多

3.4.實作AT模式

3.4.1.匯入資料庫表,記錄全域鎖

 

 

CREATE TABLE `lock_table`  (
  `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `branch_id` bigint(20) NOT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`row_key`) USING BTREE,
  INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
lock_table.sql

------------------------------------------

DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int(11) NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
undo_log.sql

 

3.4.2.修改application.yml檔案

將事務模式修改為AT模式即可

seata:
  data-source-proxy-mode: AT # 默認就是AT

2.4.3.重啟服務并測驗

4.TCC模式

TCC是Try、Confirm、Cancel三個單詞的縮寫,屬于二階段提交事務;

TCC要求每1個分支事務實作3個操作或者3個階段:Try預處理、Confirm確認、Cancel撤銷;

Try操作做業務檢查以及資源預留

Confirm做業務確認

Cancel實作1個與Try相反的操作既回滾操作

過人工編碼來實作資料恢復1個業務至少需要實作3個方介面:

  • Try:資源的檢測和預留;

  • Confirm:完成資源操作業務;要求 Try 成功 Confirm 一定要能成功,

  • Cancel:預留資源釋放,可以理解為try的反向操作, 

TCC模式的出現的慷訓歸和業務懸掛問題都是因為TM(全域事務管理器)會通過單獨的執行緒異步呼叫Try、Cancel、Confirm這3個操作

且會出現重復呼叫現象,所以TCC模式中的Try、Cancel、Confirm這3個操作都需要實作冪等性、并且避免慷訓歸和業務懸掛;

1.TCC執行流程

  • TM(業務邏輯方法)開始執行之后,立即自動向TC(Seat服務器)開啟1個發起全域事務的通知;
  • TC為當前全域事務分配1個全域事務編號;
  • TM(業務邏輯方法)方法依次向下呼叫TM業務邏輯方法里包含的RM分支事務,RM向TC上報分支事務的執行狀態,TC為RM分配分支事務編號;
  • TM業務邏輯方法呼叫完了所有RM之后,TM向TC上報執行成功/失敗通知,TC向所有RM下達分支事務的Commit/Rollback操作;

 

2.事務懸掛和慷訓滾

1.慷訓滾

慷訓滾是針對Cancel操作的,

在未執行第1階段的Try操作時,先執行了第二階段的Cancel回滾操作,說白了就是還沒有執行Try操作就執行了Cancel回滾操作,就是慷訓滾

2.慷訓滾解決方案

在執行Cancel操作之前,應當判斷Try中分支事務是否已經執行,如果尚未執行,則應該慷訓滾,

3.事務懸掛

事務懸掛是針對Try操作的;

對于已經慷訓滾的業務,之前被阻塞的try操作恢復,繼續執行try,就永遠不可能confirm或cancel ,事務一直處于中間狀態,這就是業務懸掛

4.事務懸掛解決方案

在執行第1階段的Try操作時,應當判斷第2階段的Confirm/Cancel操作是否已經執行過了?那目前就是分支事務就處于慷訓滾狀態,此時Try操作無需執行;

如果第2階段的Cancel操作已經提前執行,那目前就是分支事務就處于慷訓滾狀態,此時第1階段的Try操作,應當阻止慷訓滾之后的Try操作,避免業務懸掛,一錯再錯;

3.實作TCC模式

想要實作TCC模式就需要選擇好業務邏輯在哪里?也就是在哪里實作1個TM;

1.創建表

account_freeze_tbl

CREATE TABLE `account_freeze_tbl`  (
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `freeze_money` int(11) UNSIGNED NULL DEFAULT 0,
  `state` int(1) NULL DEFAULT NULL COMMENT '事務狀態,0:try,1:confirm,2:cancel',
  PRIMARY KEY (`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
account_freeze_tbl.sql

欄位說明

  • xid:保存全域事務ID
  • freeze_money:用來記錄用戶凍結金額
  • state:用來記錄事務狀態 

2.宣告TCC介面

TCC的Try、Confirm、Cancel方法都需要在介面中基于注解來宣告,

package cn.itcast.account.service;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;

@LocalTCC
public interface AccountService {
    /**
     * 從用戶賬戶中扣款
     */
    //void deduct(String userId, int money);
    //修改以上代碼為以下內容
    @TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money")int money);
    boolean confirm(BusinessActionContext ctx);
    boolean cancel(BusinessActionContext ctx);
}
AccountService.interface

3.宣告TCC介面

package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author 張根
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    @Transactional
    //正常邏輯
    public void deduct(String userId, int money) {
        // 0.獲取事務id
        String xid = RootContext.getXID();
        // 查詢freeze,防止業務懸掛
        AccountFreeze oldFreeze = freezeMapper.selectById(xid);
        if (oldFreeze != null) {
            // 拒絕業務
            return;
        }
        // 1.扣減可用余額
        accountMapper.deduct(userId, money);
        // 2.記錄凍結金額,事務狀態
        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        freezeMapper.insert(freeze);
    }

    //全體事務沒有出現問題
    @Override
    public boolean confirm(BusinessActionContext ctx) {
        // 1.獲取事務id
        String xid = ctx.getXid();
        // 2.根據id洗掉凍結記錄
        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }



    //全體事務中其中1個環節出現問題-回滾
    @Override
    @Transactional
    public boolean cancel(BusinessActionContext ctx) {
        // 0.查詢凍結記錄
        String xid = ctx.getXid();
        String userId = ctx.getActionContext("userId").toString();

        AccountFreeze freeze = freezeMapper.selectById(xid);
        // 慷訓滾判斷
        if (freeze == null) {
            freeze = new AccountFreeze();
            freeze.setUserId(userId);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setXid(xid);
            freezeMapper.insert(freeze);
            return true;
        }
        // 冪等判斷
        if(freeze.getState() == AccountFreeze.State.CANCEL){
            // 已經處理過了,無需重復cancel
            return true;
        }
        // 1.恢復可用余額
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        // 2.將凍結金額清零,狀態改為CANCEL
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }
}
AccountServiceImpl.java

4.TCC模式優缺點

TCC模式的每個階段是做什么的?

  • Try:資源檢查和預留

  • Confirm:業務執行和提交

  • Cancel:預留資源的釋放

TCC的優點是什么?

  • 一階段完成直接提交事務,釋放資料庫資源,性能好

  • 相比AT模型,無需生成快照,無需使用全域鎖,性能最強

  • 不依賴資料庫事務,而是依賴補償操作,可以用于非事務型資料庫

TCC的缺點是什么?

  • 有代碼侵入,需要人為撰寫try、Confirm和Cancel介面,太麻煩

  • 軟狀態,事務是最終一致

  • 需要考慮Confirm和Cancel的失敗情況,做好冪等處理

 

5.SAGA模式

在 Saga 模式下,分布式事務內有多個參與者,每一個參與者都是一個沖正補償服務,需要用戶根據業務場景實作其正向操作和逆向回滾操作,

分布式事務執行程序中,依次執行各參與者的正向操作,如果所有正向操作均執行成功,那么分布式事務提交,如果任何一個正向操作執行失敗,那么分布式事務會去退回去執行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分布式事務回到初始狀態,

Saga也分為兩個階段:

  • 一階段:直接提交本地事務

  • 二階段:成功則什么都不做;失敗則通過撰寫補償業務來回滾

5.1.優缺點

優點:

  • 事務參與者可以基于事件驅動實作異步呼叫,吞吐高

  • 一階段直接提交事務,無鎖,性能好

  • 不用撰寫TCC中的三個階段,實作簡單

缺點:

  • 軟狀態持續時間不確定,時效性差

  • 沒有鎖,沒有事務隔離,會有臟寫

 

6.Seata四種模式對比

我們從以下幾個方面來對比四種實作:

  • 一致性:能否保證事務的一致性?強一致還是最終一致?

  • 隔離性:事務之間的隔離性如何?

  • 代碼侵入:是否需要對業務代碼改造?

  • 性能:有無性能損耗?

  • 場景:常見的業務場景

 

參考

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/499913.html

標籤:其他

上一篇:設計模式遵循的設計原則

下一篇:如何實作一個狀態機?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more