事务的介绍
什么是本地事务?
当你需要一次执行多条SQL语句时,可以使用事务。通俗一点说,如果这几条SQL语句全部执行成功,则才对数据库进行一次更新,如果有一条SQL语句执行失败,则这几条SQL语句全部回滚,这个时候需要用到事务。
数据库事务的四大特性ACID
原子性(Atomicity)
要么都执行,要么都不执行
一致性(Consistency)
事务前后的数据都是正确的
隔离性(Isolation)
事物之间相互隔离,互不干扰(并发执行的事务彼此无法看到对方的中间状态)
持久性(Durability)
事务一旦提交不可再回滚
分布式事务
分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分事务、创建订单减库存事务,银行转账事务等都是分布式事务。
分布式事务解决方案之seata
Seata介绍
Seata是什么
2019年1月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),其愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。后来更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套分布式事务解决方案。
官网:Seata
Seata的分布式事务解决方案
Seata提供了四种不同的分布式事务解决方案:
XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入.
TCC模式:最终一致的分阶段事务模式,有业务侵入
AT(auto transaction)模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式.
SAGA模式:长事务模式,有业务侵入
Seata的核心组件
Seata事物管理中有三个重要的核心组件:
TC(Transaction Coordinator)-事务协调者:维护分支事务的状态,协调全局事务提交或回滚。
TM(Transaction Manager)-事务管理器:定义全局事务的范围,并开始全局事务、提交或回滚全局事务。
RM(Resource Manager)-资源管理器:向TC注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
TM是一个分布式事务的发起者和终结者,TC负责维护分布式事务的运行状态,而RM则负责本地事务的运行并上报,:
AT模式的工作流程
下面我们通过一个分支事务的执行过程来了解 Seata 的工作流程。
例如有一个业务表 product(id,name),分支事务的业务逻辑:
update product set name = 'GTS' where name = 'TXC';
一阶段
解析 SQL
得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。
查询前镜像
根据解析得到的条件信息,生成查询语句,定位数据。
select id, name from product where name = 'TXC';
得到前镜像:
id name
1 TXC
执行业务 SQL
执行自己的业务逻辑:
update product set name = 'GTS' where name = 'TXC';
把 name 改为了 GTS。
.查询后镜像
根据前镜像的结果,通过 主键 定位数据。
select id, name from product where id = 1;
得到后镜像:
id name
1 GTS
插入回滚日志
把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。
注册分支事务
RM向TC注册分支事务将其其纳入XID对应全局事务的管辖,并申请 product 表中主键值等于 1 的记录的全局锁 。
本地事务提交
业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。
上报事务状态
将本地事务提交的结果上报给 TC。
二阶段 - 回滚
(1)收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
(2)通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
(3)数据校验
拿 UNDO LOG 中的后镜与当前数据进行比较,根据校验结果决定是否做回滚。
(4)根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:
update product set name = 'TXC' where id = 1;
(5)提交本地事务
并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。
二阶段 - 提交
第二阶段全局事务提交,TC会通知各各分支参与者提交分支事务,在第一阶段就已经提交了分支事务,这里各各参与者只需要删除undo_log即可,并且可以异步执行,第二阶段很快可以完成。
Seata安装和启动
下载
下载地址:https://github.com/seata/seata/releases
安装
上传并解压安装包:
[root@localhost ~]# cd /usr/upload
[root@localhost upload]# tar -zxvf seata-server-1.4.2.tar.gz -C /usr/local
修改配置文件
修改seata/seata-server-1.4.2/conf/目录下的registry.conf文件:
registry {
#tc服务的注册中心类型,这里选择nacos,也可以是eureka、zookeeper等
type = "nacos"
nacos {
# seata tc服务注册到nacos的服务名称,可以自定义
application = "seata-server"
# nacos的地址
serverAddr = "192.168.204.129:8848"
# seata服务所在分组
group = "DEFAULT_GROUP"
# seata服务所在的名称空间,这里不填就是使用默认的"public"
namespace = ""
# TC集群名,默认是"default"
cluster = "default"
# 这个是nacos的用户名
username = ""
# 这个是nacos的密码
password = ""
}
}
config {
# tc服务的配置中心类型:file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "192.168.204.129:8848"
namespace = ""
group = "DEFAULT_GROUP"
username = ""
password = ""
#######特别注意:这个dataId需要我们看清楚 名字需要跟注册到nacos的配置中心的配置名字保持一致
#######注册时候要勾选properties选项
dataId = "seata-server.properties"
}
}
在Nacos中添加配置信息
配置信息地址:script/config-center/config.txt · Seata/seata - Gitee.com
# 数据存储方式,db代表数据库 主要的就是更改数据库的连接地址 以及账号密码
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://192.168.31.18:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=1111
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
# 事务、日志等配置
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
# 客户端与服务端传输方式
transport.serialization=seata
transport.compressor=none
# 关闭metrics功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
效果如下:
这里注册的名字需要跟上面的那个注册到nacos的配置中心的名字保持一致
建表语句
建表语句地址:script/server/db/mysql.sql · Seata/seata - Gitee.com
-- -------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
启动Seata
启动:
[root@localhost local]# cd seata/bin
[root@localhost bin]# ./seata-server.sh
#或
[root@localhost bin]# ./seata-server.sh -h 192.168.204.135 -p 8091
... ...
######
./seata-server.sh -h 192.168.204.135也可启动 seata默认端口是8091
-h的seata自身的地址 最好是加上
这里最好是加上-h 如果不加的话 可能后面注册到nacos中后会发生错误
######
io.seata.config.FileConfiguration : The configuration file used is /usr/local/seata/seata-server-1.4.2/conf/registry.conf
com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
i.s.core.rpc.netty.NettyServerBootstrap : Server started, listen port: 8091
使用Seata实现事务控制
建表语句
undo_log建表语句地址:script/client/at/db/mysql.sql · Seata/seata - Gitee.com
-- 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 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 = utf8 COMMENT ='AT transaction mode undo table';
使用依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
application.yml
seata:
registry:
type: nacos #查找TC服务,参考registry.conf
nacos:
server-addr: 192.168.204.129:8848
namespace: ""
group: DEFAULT_GROUP
application: seata-server #TC服务名
tx-service-group: seata-demo #事务组,根据tx-service-group名称获得TC服务cluster名称
service:
vgroup-mapping: #tx-service-group与TC cluster的映射关系
seata-demo: default
使用
将@GlobalTransactional放到需要的方法上面就行
#例如
@GlobalTransactional
@Override
public Long insert( ) {
表1的操作
远程调用表2的操作
//若是出现异常 将会回滚两个的数据
}
————————————————
版权声明:本文为CSDN博主「JavaOutlier」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
扫码关注旭宓科技服务号