seata 1.1.0 入門,簡介

 

1.SeaTa簡介

SeaTa 是阿里開源的可供商用的分佈式事務框架 前身Fescar , java程序

 

1.1亮點

  • 應用層基於SQL解析實現了自動補償,從而最大程度的降低業務侵入性;
  • 將分佈式事務中TC(事務協調者)獨立部署,負責事務的註冊、回滾;
  • 通過全局鎖實現了寫隔離與讀隔離
  • 多種事務模式 : AT、TCC、SAGA 事務模式

 

1.2 SeaTa相關概念

  • TC :事務協調器,維護全局事務的運行狀態,負責協調並驅動全局事務的提交或回滾。
  • TM:控制全局事務的邊界,負責開啓一個全局事務,並最終發起全局提交或全局回滾的決議。
  • RM:控制分支事務,負責分支註冊、狀態彙報,並接收事務協調器的指令,驅動分支(本地)事務的提交和回滾。
  • TC(Server端)爲單獨服務端部署,TM和RM(Client端)由業務系統集成。

 

1.3執行流程

 

  1. TM 向 TC 申請開啓一個全局事務,TC 創建全局事務後返回全局唯一的 XID,XID 會在全局事務的上下文中傳播;
  2. RM 向 TC 註冊分支事務,該分支事務歸屬於擁有相同 XID 的全局事務;
  3. TM 向 TC 發起全局提交或回滾;
  4. TC 調度 XID 下的分支事務完成提交或者回滾

 

1.4 詳細參數

https://seata.io/zh-cn/docs/user/configurations.html

 

1.5性能損耗

  • 一條Update的SQL,則需要全局事務xid獲取(與TC通訊)
  • before image(解析SQL,查詢一次數據庫)
  • after image(查詢一次數據庫)
  • insert undo log(寫一次數據庫)
  • before commit(與TC通訊,判斷鎖衝突)

這些操作都需要一次遠程通訊RPC,而且是同步的。另外undo log寫入時blob字段的插入性能也是不高的。每條寫SQL都會增加這麼多開銷,粗略估計會增加5倍響應時間(二階段雖然是異步的,但其實也會佔用系統資源,網絡、線程、數據庫)

 

 

 

2.準備工作

 

1.server端工作

 

1.TC (Seate-server ) 下載

2.建表

  • TC需要新建三張表
    • db_store.sql
    • 各個表對應功能
      • 全局事務---global_table
      • 分支事務---branch_table
      • 全局鎖-----lock_table

 

3.修改配置文件

  • seata/conf/file.conf server服務 的日誌記錄方式,數據庫連接信息


## transaction log store, only used in seata-server
store {
  ## store mode: file、db 事務日誌存儲模式
  mode = "db"   
# 服務端配置
 service {
# 分組名稱 需要和client端一致 chuangqi-steata
  vgroup_mapping.chuangqi-steata = "chuangqi-steata"
  chuangqi-steata.grouplist = "127.0.0.1:8091"
# 降級開關 默認關閉
  enableDegrade = false
  disable = false
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}
  ## file store property 
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }




  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://localhost:3306/seata"
    user = "root"
    password = "root"
    minConn = 1
    maxConn = 10
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
  

 

  • seata/conf/registry.conf 不同註冊中心和配置中心 file 單機本地版
registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "file"




  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}




config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"




  nacos {
    serverAddr = "localhost"
    namespace = ""
    group = "SEATA_GROUP"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  

 

4.啓動

seata/bin/seata-server.sh

nohup sh seata-server.sh -h xx.xx.xx.xx -p 8091 -m db -n 1 -e test &
-h: 註冊到註冊中心的ip
-p: Server rpc 監聽端口
-m: 全局事務會話信息存儲模式,file、db,優先讀取啓動參數
-n: Server node,多個Server時,需區分各自節點,用於生成不同區間的transactionId,以免衝突
-e: 多環境配置參考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html

 

 

 

2.client端工作

 

2.1項目添加依賴 (單選) 

  • 依賴seata-all 手動配置較多
  • 依賴seata-spring-boot-starter,支持yml配置
  • 依賴spring-cloud-alibaba-seata,內部集成了seata,並實現了xid傳遞
  • client 版本與 server端版本一致

 

 

2.2項目新建undo_log表

db_undo_log.sql

 

2.3 增加配置文件 (1.1.0版本)

  • yml文件
seata:
  enabled: true
  application-id: account-api  # 項目標識
  tx-service-group: chuangqi-steat # seata分組名稱
  enable-auto-data-source-proxy: true # 開啓數據源自動代理
  use-jdk-proxy: false # 使用的代理方式
  client:
    rm:
      async-commit-buffer-limit: 1000
      report-retry-count: 5
      table-meta-check-enable: false
      report-success-enable: false
      lock:
        retry-interval: 10
        retry-times: 30
        retry-policy-branch-rollback-on-conflict: true
    tm:
      commit-retry-count: 5
      rollback-retry-count: 5
    undo:
      data-validation: true
      log-serialization: jackson
      log-table: undo_log
    log:
      exceptionRate: 100
  service:
    vgroup-mapping:
      my_test_tx_group: default
    grouplist:
      default: 127.0.0.1:8091
    #enable-degrade: false
    #disable-global-transaction: false
  transport:
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      server-executor-thread-prefix: NettyServerBizHandler
      share-boss-worker: false
      client-selector-thread-prefix: NettyClientSelector
      client-selector-thread-size: 1
      client-worker-thread-prefix: NettyClientWorkerThread
      worker-thread-size: default
      boss-thread-size: 1
    type: TCP
    server: NIO
    heartbeat: true
    serialization: seata
    compressor: none
    enable-client-batch-send-request: true
  config:
    type: file
    consul:
      server-addr: 127.0.0.1:8500
    apollo:
      apollo-meta: http://192.168.1.204:8801
      app-id: seata-server
      namespace: application
    etcd3:
      server-addr: http://localhost:2379
    nacos:
      namespace:
      serverAddr: localhost
      group: SEATA_GROUP
    zk:
      server-addr: 127.0.0.1:2181
      session-timeout: 6000
      connect-timeout: 2000
      username: ""
      password: ""
  registry:
    type: file
    consul:
      cluster: default
      server-addr: 127.0.0.1:8500
    etcd3:
      cluster: default
      serverAddr: http://localhost:2379
    eureka:
      application: default
      weight: 1
      service-url: http://localhost:8761/eureka
    nacos:
      cluster: default
      server-addr: localhost
      namespace:
    redis:
      server-addr: localhost:6379
      db: 0
      password:
      cluster: default
      timeout: 0
    sofa:
      server-addr: 127.0.0.1:9603
      application: default
      region: DEFAULT_ZONE
      datacenter: DefaultDataCenter
      cluster: default
      group: SEATA_GROUP
      addressWaitTime: 3000
    zk:
      cluster: default
      server-addr: 127.0.0.1:2181
      session-timeout: 6000
      connect-timeout: 2000
      username: ""
      password: ""

 

2.4 開啓數據源代理

  • 關閉spring自動代理
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
  • 注入數據源
@Bean
@Autowired
public SqlSessionFactory sqlsessionfactory(HikariDataSource dataSource, Configuration configuration) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);


    sqlSessionFactoryBean.setPlugins(new Interceptor[]{new PageInterceptor()});
    sqlSessionFactoryBean.setConfiguration(configuration);
    return sqlSessionFactoryBean.getObject();
}


@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource() {
   return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSource(DataSource druidDataSource){
        return new DataSourceProxy(druidDataSource);
    }

 

2.4 初始化GlobalTransactionScanner

  • 手動
       public GlobalTransactionScanner globalTransactionScanner() {
           String applicationName = this.applicationContext.getEnvironment().getProperty("spring.application.name");
           String txServiceGroup = this.seataProperties.getTxServiceGroup();
           if (StringUtils.isEmpty(txServiceGroup)) {
               txServiceGroup = applicationName + "-fescar-service-group";
               this.seataProperties.setTxServiceGroup(txServiceGroup);
           }
   
           return new GlobalTransactionScanner(applicationName, txServiceGroup);
       }
  • 自動,引入seata-spring-boot-starter、spring-cloud-alibaba-seata等jar

 

 

 

2.5根據項目架構 配置 XID 傳遞

  • 手動 參考源碼integration文件夾下的各種rpc實現 module
  • 自動 springCloud用戶可以引入spring-cloud-alibaba-seata,內部已經實現xid傳遞

 

 

2.6 使用

  • 1.@GlobalTransaction 全局事務註解 項目中要實現分佈式事務的接口加入
  • 2.@GlobalLock 防止髒讀和髒寫,又不想納入全局事務管理時使用。(不需要rpc和xid傳遞等成本)

 

3.注意事項

 

1.server端

  • file模式爲單機模式,全局事務會話信息內存中讀寫並持久化本地文件root.data,性能較高;
  • db模式爲高可用模式,全局事務會話信息通過db共享,相應性能差些
  • file模式可直接啓動 , 無需多餘配置

 

2.client端

  • restTemplate RPC調用時 : SeataFilter ,SeataRestTemplateAutoConfiguration 需要交給spring管理 , 掃描包路徑記得添加

 

 

4.各個模式介紹

 

1.AT模式

 

1.0簡介

  • 簡單上手
  • 二階段提交

 

1.1 前提

  • 基於支持本地 ACID 事務的關係型數據庫。
  • Java 應用,通過 JDBC 訪問數據庫

 

1.2實現

  • 二階段提交演變 , 整體基於本地事務 + 全局鎖 + 本地鎖 實現 回滾通過undo_log進行反向補償
  • 一階段:業務數據和回滾日誌記錄在同一個本地事務中提交,釋放本地鎖和連接資源。
  • 二階段:
    • 提交異步化,非常快速地完成。
    • 回滾通過一階段的回滾日誌進行反向補償。

 

 

1.3 步驟

  • 一階段開啓事務時 首先獲取本地鎖 然後開始走業務邏輯
    • 本地鎖獲取失敗 不斷重試
  • 一階段提交前 , 首先嚐試獲取該條記錄的全局鎖
    • 獲取全局鎖失敗 不能提交本地事務 , 重試獲取全局鎖 (獲取 默認 10ms一次 , 重試30次)
      • client.rm.lock.retryInterval 校驗或佔用全局鎖重試間隔 默認10 單位ms
      • client.rm.lock.retryTimes 校驗或佔用全局鎖重試次數 默認30
      • 重試後仍未獲取到全局鎖 , 回滾本地事務 , 釋放本地鎖
    • 獲取成功 一階段事務執行
  • 二階段事務執行
    • commit : 直接執行
    • rollback : 首先重新獲取 該條記錄的本地鎖 , 然後執行反向補償操作 實現回滾

 

 

1.4 髒寫,髒讀問題

1.寫隔離

  • 上述案例, 在tx1 二階段提交之前 tx2可以獲取到tx1還未全局提交的數據 ,tx2提交事務會髒寫
    • 解決 :
    • tx1二階段提交前一直擁有全局鎖 , 回滾時需要獲取本地鎖 ,
    • tx2 擁有本地鎖 , 一階段提交前需要獲取到全局鎖
    • tx1回滾獲取本地鎖會不斷重試 , 那麼tx2獲取全局鎖會超時
    • tx2超時 -> 回滾本地事務 , 釋放本地鎖
    • tx1獲取到本地鎖 -> 事務回滾

2.讀隔離

  • 上述案例 , tx2是否可以讀到 tx1還未全局提交的數據
    • 在數據庫本地事務隔離級別 讀已提交(Read Committed) 或以上的基礎上,Seata(AT 模式)的默認全局隔離級別是 讀未提交(Read Uncommitted) 。
    • 通過 SELECT FOR UPDATE實現
    • SELECT FOR UPDATE 會去申請全局鎖
      • 獲取失敗 則重試獲取 直到全局鎖獲取到
  • 對性能消耗較大
  • @GlobalLock 註解解決髒讀幻讀問題 (不生成undo_log)

 

1.5工作原理

# 分支事務的sql
update product set name = 'GTS' where name = 'TXC';

一階段

  • 解析sql , 通過條件獲取到查詢前鏡像數據
    • select id, name, since from product where name = 'TXC'; 獲取到查詢前鏡像
  • 執行sql
  • 獲取執行後鏡像 , 根據查詢前鏡像結果 , 通過主鍵 定位查詢後鏡像
    • select id, name, since from product where id = 1`;
  • 把鏡像前後數據 以及業務sql等信息 組成一條 回滾記錄 插入到undo_log中
{
	"branchId": 641789253,
	"undoItems": [{
		"afterImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "GTS"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"beforeImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "TXC"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"sqlType": "UPDATE"
	}],
	"xid": "xid:xxx"
}
  • 提交前,向 TC 註冊分支:申請 product 表中,主鍵值等於 1 的記錄的 全局鎖 。
  • 本地事務提交:業務數據的更新和前面步驟中生成的 UNDO LOG 一併提交。
  • 將本地事務提交的結果上報給 TC。

 

二階段-回滾

  • 收到 TC 的分支回滾請求,開啓一個本地事務,執行如下操作。
  • 通過 XID 和 Branch ID 查找到相應的 UNDO LOG 記錄。
  • 數據校驗:拿 UNDO LOG 中的後鏡與當前數據進行比較,如果有不同,說明數據被當前全局事務之外的動作做了修改。這種情況,需要根據配置策略來做處理
  • 根據 UNDO LOG 中的前鏡像和業務 SQL 的相關信息生成並執行回滾的語句:
update product set name = 'TXC' where id = 1;
  • 提交本地事務。並把本地事務的執行結果(即分支事務回滾的結果)上報給 TC。

 

二階段-提交

  • 收到 TC 的分支提交請求,把請求放入一個異步任務的隊列中,馬上返回提交成功的結果給 TC。
  • 異步任務階段的分支提交請求將異步和批量地刪除相應 UNDO LOG 記錄。

 

1.6特點

  • 改造成本低 , cloud項目基本只需要添加配置文件 , 新增註解
  • 普通springboot項目 - 添加配置文件 , 實現XID傳遞
  • 隔離性

 

2.TCC模式

 

2.0簡介

  • 二階段提交

 

2.1前提

  • 每個分支事務需要具備
    • 一階段 prepare 方法 (本地提交)
    • 二階段 commit 或 rollback 方法
  • TCC模式不依賴底層數據源的事務支持
    • 一階段 prepare 行爲:調用 自定義 的 prepare 邏輯。
    • 二階段 commit 行爲:調用 自定義 的 commit 邏輯。
    • 二階段 rollback 行爲:調用 自定義 的 rollback 邏輯。
  • TCC其實就是 自定義本地事務 加入了全局事務的管理

 

2.2特點

  • 性能好 , 沒有多餘的操作, 只有TC管理
  • 隔離性
  • 業務改動大 , 開發困難

 

 

3.Saga模式

 

3.0簡介

  • SEATA提供的長事務解決方案
  • 業務流程中每個參與者都提交本地事務
  • 當出現某一個參與者失敗則補償前面已經成功的參與者
  • 一階段正向服務 和 二階段補償服務都由業務開發實現

 

3.1前提

  • 開發一階段正向業務 和 二階段補償業務

 

3.2特點

  • 適合長事務
  • 參與者包含其它公司或遺留系統服務,無法提供 TCC 模式要求的三個接口
  • 一階段提交本地事務,無鎖,高性能
  • 事件驅動架構,參與者可異步執行,高吞吐
  • 不保證隔離性

 

3.3實現

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章