1.事务简介
事务是访问并可能更新数据库中各种数据项的一个程序执行单元。在关系型数据库中,一个事务由一组sql语句组成。事务具有 原子性,一致性,隔离性,持久性四个属性(ACID)。
原子性:事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一组Sql中,执行要么都成功,要么都失败。
一致性:事务必须是使数据库从一个一致性状态变更到另一个一致性状态,事务的中间状态不能被观察到。
一组Sql执行后,不管是提交还是回滚,数据都是一致的。
隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的。并发执行的各个事务之间不能相互干扰。隔离性又分为四个级别:读未提交(read uncommitted),读已提交(read commited,解决脏读),可重复读(repeatable read,解决虚读),串行化(serializable,解决幻读)。
持久行:事务一旦提交,它对数据库中数据的改变就应该是永久性的。
事务分为本地事务(单体项目中使用的@Transational)和分布式事务。
2.Seata介绍
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式。
官网地址:https://seata.apache.org/zh-cn/docs/overview/what-is-seata/
2.1)Seata的底层原理
在seata的架构中,一共有三个角色:
TC-事务协调者,TM-事务管理器,RM-资源管理器。
TM发起全局事务,调用@GlobalTransactional方法,向TC注册全局事务(生成XID)。RM执行分支事务,每个RM执行本地SQL,生成undo_log,提交本地事务,并向TC上报分支事务状态。TC协调全局状态,TC汇总所有分支事务状态,等待TM决策。TM决策提交/回滚,若所有分支成功,TM通知TC提交全局事务,TC通知各个RM删除undo_log。若所有分支失败,TM通知TC回滚,TC通知各个RM根据undo_log回滚。
涉及到的表 branch_table
global_table
lock_table
其中TC为单独部署的Server服务端,TM和RM为嵌入到应用中的Client客户端。
2.2)传统分布式事务解决方案(2PC)
两阶段(2PC),两阶段是指完成整个分布式事务,划分成两个步骤完成
即 Prepare(提交事务请求)/commit/rollback(执行事务提交/回滚)。
第一阶段:
Prepare(提交事务请求):
1.询问:协调者向所有参与者发送事务请求,询问是否可执行事务操作,然后等待各个参与者响应。
2.执行:各个参与者接收到协调者事务请求后,执行事务操作(例如更新一个关系型数据库表中的记录),并将undo(元数据)和redo(提交数据)信息记录到 undo_log。
3.响应:如果参与者成功执行了事务并写入undo(元数据)和redo(提交数据)信息,则向协调者返回YES响应,否则返回NO响应。如果参与者宕机了,就不会返回响应。
第二阶段:
commit/rollback(执行事务提交/回滚):
正常提交事务:
1.commit:协调者向所有参与者发送commit请求。
2.事务提交:所有参与者收到commit请求后,执行事务提交(redo),提交完成后释放事务执行期占用的所有资源。
3.反馈结果:所有参与者事务提交后,向协调者发送响应。
4.完成事务:接收到所有参与者的响应后,完成事务提交。
中断事务:第一阶段执行过程中,部分参与者响应NO或者没有响应。
1.rollback:协调者向所有参与者发送rollback请求。
2.事务回滚:参与者收到rollback后,使用undo日志执行事务回滚,完成后释放事务执行期占用的所有资源。
3.反馈结果:所有参与者事务回滚后,向协调者发送响应。
4.中断事务:接收到所有参与者的响应后,完成事务中断。
2PC的问题:
1.同步阻塞。参与者在等待协调者的指令时,其实是在等待其他参与者的响应,在此过程中,参与者是无法进行其他操作,也就阻塞了其运行。
2.数据不一致。当协调者突然宕机,部分参与者没有接收到 commit/rollback请求,另一部分参与者接收到commit/rollback请求。这时数据就不再一致。
2.3)Seata AT模式(auto transcation)
AT模式是一种无侵入的分布式事务解决方案。
在AT模式下,用户只需要关注自己的业务SQL,用户的业务SQL作为第一阶段,Seata框架会自动生成事务的二阶段提交和回滚操作。
第一阶段-分支事务提交(RM):
在第一阶段RM执行业务SQL,操作数据库。RM拦截用户的业务SQL,解析业务SQL。在业务SQL执行前,将其保存成“before image”,在执行业务SQL后,再将其保存成“after image”,存入undo_log表中。分支事务直接提交后会释放本地锁保留全局锁并将分支事务状态上报TC。
第二阶段全局事务提交/回滚:
全局事务提交,TC会通知RM异步删除undo_log记录。
全局事务回滚,在回滚前RM会检查当前数据库中的实际数据是否与after image一致,一致则根据 undo_log 逆向生成补偿 SQL,否则说明数据被其他事物修改过报异常。
3.搭建Seata-server
3.1)seata-server(TC)搭建(db存储模式+Nacos部署)
seata官网部署指南:https://seata.apache.org/zh-cn/docs/ops/deploy-guide-beginner/
下载安装包:https://github.com/apache/incubator-seata/releases
本人Spring Cloud Alibaba版本是 2023.0.0.0-RC1所以选择Seata版本为seata-server-2.0.0.zip
资源目录地址:https://github.com/apache/incubator-seata/tree/master/script
client:存放client端sql脚本 (包含 undo_log表) ,参数配置
config-center:各个配置中心参数导入脚本,config.txt(包含server和client,原名nacos-config.txt)为通用参数文件
server:server端数据库脚本 (包含 lock_table、branch_table 与 global_table) 及各个容器配置
Server端存储模式(store.mode)现有file、db、redis、raft。file模式无需改动,直接启动即可。
raft部署方式可查看:https://seata.apache.org/zh-cn/docs/ops/deploy-server-raft/
注:
file模式为单机模式,全局事务会话信息内存中读写并异步(默认)持久化本地文件root.data,性能较高;
db模式为高可用模式,全局事务会话信息通过db共享,相应性能差些;
redis模式Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置合适当前场景的redis持久化配置.
3.1.1)修改Server端配置
配置文件目录位置:seata\conf\application.yml。配置信息可以参考application.example.yml
1.存储模式: db
2.config(配置中心)/registry(注册中心):nacos
3.nacos配置seataServer.properties
配置信息如下:
# Copyright 1999-2019 Seata.io Group.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${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
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace:
group: SEATA_GROUP
username: nacos
password: nacos
##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
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace:
cluster: default
username: nacos
password: nacos
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
store:
# support: file 、 db 、 redis 、 raft
mode: db
session:
mode: db
lock:
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&allowMultiQueries=true
user: root
password: 123456
min-conn: 10
max-conn: 100
global-table: global_table
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 1000
max-wait: 5000
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**
创建数据库导入mysql脚本:
数据库脚本位置:seata\script\server\db
nacos配置seataServer.properties:
将config内容复制到nacos中并修改数据源信息。
文件位置:seata\script\config-center
3.1.2)启动并访问Server端
双击运行:seata-server.bat
目录位置:seata\bin
访问seata控制台:http://127.0.0.1:7091 账密默认为seata/seata
4.搭建Seata Client(项目整合Seata)
4.1)添加Seata依赖
<!-- SpringCloud Alibaba seata-->
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies>
4.2)每个服务对应的数据库中添加undo_log表
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) 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';
ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);
4.3)服务yml配置seata
#seata
seata:
#事务分组 要和nacos seataServer.properties 中的service.vgroupMapping.default_tx_group 一致
tx-service-group: default_tx_group
registry:
#配置seata的注册中心,告诉 seata client 怎么去访问seata service
type: nacos
nacos:
server-addr: 127.0.0.1:8848
#seata service 的 application: name
application: seata-server
username: nacos
password: nacos
group: SEATA_GROUP
config:
#配置seata的配置中心,可以读取关于seata client 的一些配置
type: nacos
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
group: SEATA_GROUP
data-id: seataServer.properties