seata1.5.2
1. Seata是什么?
Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和XA 事務模式,為用戶打造一站式的分布式解決方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事務服務)。
2. Seata的三大角色
在 Seata 的架構中,一共有三個角色:
TC (Transaction Coordinator) - 事務協調者
維護全局和分支事務的狀態,驅動全局事務提交或回滾。
TM (Transaction Manager) - 事務管理器
定義全局事務的范圍:開始全局事務、提交或回滾全局事務。
RM (Resource Manager) - 資源管理器
管理分支事務處理的資源,與TC交談以注冊分支事務和報告分支事務的狀態,并驅動分支事務提交或回滾
3. Seata的模式
- AT
- TCC
- Sage
- XA
這邊就不過多說明,我們這邊主要使用的是AT模式,這個也是seata開源版本的選擇,基于2階段提交的思想
官網:https://seata.io/zh-cn/index.html
源碼: https://github.com/seata/seata
官方Demo: https://github.com/seata/seata-samples
4. 設計亮點
相比與其它分布式事務框架,Seata架構的亮點主要有幾個:
- 應用層基于SQL解析實現了自動補償,從而最大程度的降低業務侵入性;
- 將分布式事務中TC(事務協調者)獨立部署,負責事務的注冊、回滾;
- 通過全局鎖實現了寫隔離與讀隔離。
5. 存在的問題
性能損耗
一條Update的SQL,則需要全局事務xid獲取(與TC通訊)、before image(解析SQL,查詢一次數據庫)、after image(查詢一 次數據庫)、insert undo log(寫一次數據庫)、before commit(與TC通訊,判斷鎖沖突),這些操作都需要一次遠程通訊RPC,而 且是同步的。另外undo log寫入時blob字段的插入性能也是不高的。每條寫SQL都會增加這么多開銷,粗略估計會增加5倍響應時間。
性價比
為了進行自動補償,需要對所有交易生成前后鏡像并持久化,可是在實際業務場景下,這個是成功率有多高,或者說分布式事務失敗 需要回滾的有多少比率?按照二八原則預估,為了20%的交易回滾,需要將80%的成功交易的響應時間增加5倍,這樣的代價相比于讓應 用開發一個補償交易是否是值得?
全局鎖
熱點數據 相比XA,Seata 雖然在一階段成功后會釋放數據庫鎖,但一階段在commit前全局鎖的判定也拉長了對數據鎖的占有時間,這個開銷 比XA的prepare低多少需要根據實際業務場景進行測試。全局鎖的引入實現了隔離性,但帶來的問題就是阻塞,降低并發性,尤其是熱點 數據,這個問題會更加嚴重。
回滾鎖釋放時間 Seata在回滾時,需要先刪除各節點的undo log,然后才能釋放TC內存中的鎖,所以如果第二階段是回滾,釋放鎖的時間會更長。
死鎖問題 Seata的引入全局鎖會額外增加死鎖的風險,但如果出現死鎖,會不斷進行重試,最后靠等待全局鎖超時,這種方式并不優雅,也延 長了對數據庫鎖的占有時間。
6. Seata快速開始 (默認nacos-server2.1.0已經已經安裝配置好,我的nacos是安裝在win10當中的,單機模式 127.0.0.1:8848)
1 Seata Server(TC)環境搭建
下載安裝包 https://github.com/seata/seata/releases 下載1.5.2版本(各個版本安裝有所不同,且seata版本要與alibaba版本對應 請知悉)

解壓seata1.5.2的安裝包

主要關注截圖的 bin conf lib script 目錄
bin 啟動命令 (.sh為liunx .bat為win)

conf 目錄為配置文件目錄 (日志配置這邊就不多說了,)

application.example.yml 這個為模版了,按照這個配置就行
application.yml 默認的是使用內置的file方式啟動的,可以啟動,但只適合單機配置,就不記錄這種方式了(當初接觸seata0.9版本的時候,玩的就是file模式,需要修改的配置就很多,現在1.5.2版本都集成到配置文件上了,方便簡化了很多)
我就直接上修改好的配置文件了
server: port: 7091 # 默認端口就不修改了 spring: application: name: seata-server # 默認服務名稱 就不修改了 logging: config: classpath:logback-spring.xml # 日志配置 file: path: ${user.home}/logs/seata extend: logstash-appender: destination: 127.0.0.1:4560 kafka-appender: bootstrap-servers: 127.0.0.1:9092 topic: logback_to_logstash console: # 控制臺的用戶名密碼 user: username: seata password: seata seata: config: # support: nacos 、 consul 、 apollo 、 zk 、 etcd3 seata的配置中心方式 nacos的配置中心就不多說 都是常規配置 指定了nacos的用戶密碼那配置文件需要寫出來 type: nacos nacos: server-addr: 127.0.0.1:8848 namespace: group: SEATA_GROUP username: password: ##if use MSE Nacos with auth, mutex with username/password attribute #access-key: "" #secret-key: "" data-id: seataServer.properties registry: # support: nacos 、 eureka 、 redis 、 zk 、 consul 、 etcd3 、 sofa type: nacos preferred-networks: 30.240.* nacos: application: seata-server server-addr: 127.0.0.1:8848 group: SEATA_GROUP namespace: cluster: default username: password: ##if use MSE Nacos with auth, mutex with username/password attribute #access-key: "" #secret-key: "" server: service-port: 8091 #If not configured, the default is '${server.port} + 1000' seata服務的配置 我這邊都用的模版的默認配置 max-commit-retry-timeout: -1 max-rollback-retry-timeout: -1 rollback-retry-timeout-unlock-enable: false enable-check-auth: true enable-parallel-request-handle: true retry-dead-threshold: 130000 xaer-nota-retry-timeout: 60000 vgroup-mapping: fsp_tx_group: default recovery: handle-all-session-period: 1000 undo: log-save-days: 7 log-delete-period: 86400000 session: branch-async-queue-size: 5000 #branch async remove queue size enable-branch-async-remove: false #enable to asynchronous remove branchSession store: # support: file 、 db 、 redis seata數據的保存方式,這邊后續考慮高可用 選擇使用數據庫保存 默認是file文件 mode: db db: datasource: druid db-type: mysql driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://110.41.42.252:3308/seata_server?rewriteBatchedStatements=true user: root password: 123456 min-conn: 5 max-conn: 100 global-table: global_table branch-table: branch_table lock-table: lock_table distributed-lock-table: distributed_lock query-limit: 100 max-wait: 5000 security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

lib/jdbc目錄下有2個mysql的驅動包一個是5.X 一個是8.X 這邊最刪除掉不用的驅動包(mysql服務的版本查看這邊就不多說了)

新建一個數據庫我這邊叫 seata_server 與配置文件對應起來 在mysql服務中執行mysql.sql 會生成4張表

表具體的作用就不過多說明
客戶端連接的數據庫需要增加日志表,方便數據的回滾,這邊也記錄下來 熟悉mysql的對undo.log 應該不陌生
-- for AT mode you must to init this sql for you business database. the seata server not need it. CREATE TABLE IF NOT EXISTS `undo_log` ( `branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id', `context` VARCHAR(128) 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 KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
接下來就可以啟動.bat 文件了 liunx就是.sh

前提是nacos已經啟動完成,且配置nacos正確 seata1.5.2對應nacos版本為2.1.0 版本要對應起來
nacos

seata

到此 seata服務就啟動完成了 后續就到整合使用了
控制臺地址: Http://127.0.0.1:7091

客戶端搭建
這邊就不多說直接上代碼
創建父maven工程
pom
<properties>
<java.version>8</java.version>
<!-- Spring Cloud -->
<spring.cloud.version>Hoxton.SR12</spring.cloud.version>
<!-- Spring Boot -->
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<!-- Spring Cloud alibaba -->
<project.version>2.2.9.RELEASE</project.version>
</properties>
<!--Spring Cloud Alibaba Version-->
<!--2.2.9.RELEASE-->
<!--Sentinel Version-->
<!--1.8.5-->
<!--Nacos Version-->
<!--2.1.0-->
<!--RocketMQ Version-->
<!--4.9.4-->
<!--seata Version-->
<!--1.5.2-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- Spring Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
事務分支1
pom
<dependencies>
<!--seata 分布式事務組件-->
<!--<dependency>-->
<!--<groupId>com.alibaba.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-alibaba-seata</artifactId>-->
<!--</dependency>-->
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web組件+actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--數據庫連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--MySQL驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatisPlus 核心庫 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<!--seata 分布式事務組件-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
application.yml
server: port: 8090 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://110.41.42.252:3308/test003?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai username: root password: 123456 application: name: mybatis-service cloud: nacos: discovery: namespace: public server-addr: 127.0.0.1:8848 #開啟日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 開放端口 management: endpoints: web: exposure: include: "*" # seata日志級別 logging: level: io: seata: info seata: registry: # 配置seata的注冊中心, 告訴seata client 怎么去訪問seata server(TC) type: nacos nacos: server-addr: 127.0.0.1:8848 # seata server 所在的nacos服務地址 application: seata-server # seata server 的服務名seata-server ,如果沒有修改可以不配 username: nacos password: nacos group: SEATA_GROUP # seata server 所在的組,默認就是SEATA_GROUP,沒有改也可以不配 config: type: nacos nacos: server-addr: 127.0.0.1:8848 username: nacos password: nacos group: SEATA_GROUP tx-service-group: fsp_tx_group #這里每個服務都是對應不同的映射名,在配置中心可以看到 service: vgroup-mapping: fsp_tx_group: default
最基礎的代碼就不寫了
事務分支2 使用openfeign接口調用事務分支1
pom
<dependencies>
<!--seata 分布式事務組件-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
<!--openfeign客戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web組件+actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--數據庫連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--MySQL驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatisPlus 核心庫 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
application.yml
server: port: 8092 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://110.41.42.252:3308/test002?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai username: root password: 123456 application: name: seata-service cloud: nacos: discovery: namespace: public server-addr: 127.0.0.1:8848 #開啟日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 開放端口 management: endpoints: web: exposure: include: "*" # seata日志級別 logging: level: com.nicong.feign: debug io: seata: info # feign饑餓加載配置 ribbon: eager-load: enabled: true clients: mybatis-service feign: client: config: mybatis-service: #服務名稱 connectTimeout: 5000 #默認2秒 readTimeout: 10000 #默認5秒: loggerLevel: full seata: registry: # 配置seata的注冊中心, 告訴seata client 怎么去訪問seata server(TC) type: nacos nacos: server-addr: 127.0.0.1:8848 # seata server 所在的nacos服務地址 application: seata-server # seata server 的服務名seata-server ,如果沒有修改可以不配 username: nacos password: nacos group: SEATA_GROUP # seata server 所在的組,默認就是SEATA_GROUP,沒有改也可以不配 config: type: nacos nacos: server-addr: 127.0.0.1:8848 username: nacos password: nacos group: SEATA_GROUP tx-service-group: fsp_tx_group #這里每個服務都是對應不同的映射名,在配置中心可以看到 service: vgroup-mapping: fsp_tx_group: default
啟動類需要額外加上
@EnableFeignClients 開啟遠程調用支持
具體的代碼實現就不寫了 基礎性的東西
現在最重要的一步
給事務分組
按照配置文件中的
tx-service-group: fsp_tx_group #這里每個服務都是對應不同的映射名,在配置中心可以看到
fsp_tx_group: default
登錄到naocs中配置
service.vgroupMapping.fsp_tx_group:default

保存發布就可以了
啟動2個事務分支(確保naocs seata正常)
觀查 seata窗口:
啟動分支1 可以看到seata已經生成 TM RM維護分支

啟動分支2 同樣可以看到TM RM生成

上面提到過每個事務分支的數據庫需要加上日志表的不要忘記,后續的演示就不截圖了
在service 實現方法接口上加上 @GlobalTransactional 就可以了
使用feign接口調用另外一個接口如果報錯,數據庫的數據都會對應的進行回滾,基于日志表
打完收工