Spring Cloud Alibaba(五) -分布式事务Seata
·
Spring Cloud Alibaba(五) -分布式事务Seata
什么是事务?(本地事务篇)
想象一下,你正在谈恋爱(执行SQL)。
- 事务:就是“要么结婚(全部提交),要么分手(全部回滚)”,绝对不能出现“证领了但彩礼没给”这种中间状态。
事务的四大金刚(ACID):
- 原子性:最小执行单位,不可分割。就像原子弹,要么不爆,爆了就是全爆。
- 一致性:不管怎么折腾,数据最后得是对的。你不能凭空变出钱来。
- 隔离性:你的事务是你的事,我的事务是我的事,咱俩别互相干扰(除非你想搞事情)。
- 持久性:一旦提交,就是板上钉钉,天王老子来了也改不了(除非删库跑路)。
MySQL 的“后悔药”机制(Undo Log):
BEGIN TRANSACTION;:开始搞事,先拿个小本本(undo_log)记下来老数据长啥样。SQL1; SQL2;:疯狂操作。COMMIT;:搞定!把小本本撕了,不用回滚了。ROLLBACK;:哎呀翻车了!赶紧掏出小本本,把数据恢复原状。
Spring 的 AOP 大法:
以前我们要自己写 try-catch 来控制事务,累得像条狗。现在有了 @Transactional 注解,Spring 就像个贴心的管家,自动帮你处理 commit 和 rollback。
分布式事务:当“本地”不够用了
场景:
以前是一个服务独大(单体应用),现在流行微服务,大家分家了。
- 尴尬的情况:订单服务(Order)扣了库存,结果积分服务(Point)挂了。订单说“我扣了”,积分说“我还没加”,数据这就“精神分裂”了。
这就是分布式事务要解决的痛点: 怎么让这几个分家了的微服务,还能像亲兄弟一样“同生共死”?
Seata 闪亮登场(核心组件篇)
Seata 就是来解决这个“家庭纠纷”的居委会大妈。它有三个核心角色:
TM - 事务管理器:发号施令的“老板”
- 职责:定义全局事务的范围。
- 口头禅:“小的们,干活了!
@GlobalTransactional开启!” - 位置:通常在业务入口(比如订单服务)。
TC - 事务协调器:操碎了心的“管家”
- 职责:维护全局和分支事务的状态,指挥 RM 是提交还是回滚。
- 装备:它自己有几张表(
global_table,branch_table,lock_table),专门用来记流水账。 - 位置:独立部署的服务器(Seata Server)。
RM - 资源管理器:干苦力的“打工仔”
- 职责:向 TC 注册分支事务,干完活汇报状态。如果 TC 说“回滚”,它就得乖乖拿出自己的
undo_log恢复数据。 - 装备:每个参与的服务数据库里,都得有一张
undo_log表(这是 Seata 的“后悔药”)。
️ 实战:Seata 安装与配置(保姆级教程)
光说不练假把式,咱们来把这个“管家”请回家。
第一步:请神(下载 Seata Server)
去 GitHub 下载 Seata Server(也就是 TC)。
- 下载地址:找最新的 release 版本,比如
seata-server-1.x.x.zip。 - 解压后,你会看到
bin,conf,lib等目录。
第二步:装修(配置 TC)
修改 conf/registry.conf 文件。
- 注册中心:如果你用 Nacos,就把
type改成nacos,填上 Nacos 的地址。
registry {
#tc服务的注册中心类型,这里选择nacos,也可以是eureka、zookeeper等
type = "nacos"
nacos {
# seata tc服务注册到nacos的服务名称,可以自定义
application = "seata-server"
# nacos的地址
serverAddr = "192.168.204.129:8848"
# seata服务所在分组
group = "SEATA_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 = "SEATA_GROUP"
username = ""
password = ""
dataId = "seataServer.properties"
}
}
- 配置中心:让 Seata 去 Nacos 里读取配置。
# 数据存储方式,db代表数据库,在此处修改mysql信息
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.20.9:3306/seata?useUnicode=true&rewriteBatchedStatements=true&useSSL=false
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
第三步:建账(数据库准备)
这一步最重要,别忘了!
- TC 的库:创建一个数据库(比如叫
seata),运行官方提供的 SQL 脚本,生成global_table等三张表。不想下载在下面复制也可以。
-- -------------- 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;
- RM 的库:在你的业务数据库(订单库、库存库等)里,必须创建
undo_log表!
undo_log 建表 SQL(复制拿走不谢):
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`),
KEY `ix_log_created` (`log_created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AT 事务模式回滚日志表';
第四步:启动(Run!)
- 进入
bin目录。 - Windows 双击
seata-server.bat。 - Linux/Mac 执行
sh seata-server.sh或sh seata-server.sh -h ip地址 -p 端口号(用此居多)。 - 看到控制台打印出 “Seata Server started…” 就算成功了!
第五步:微服务接入(RM 端配置)
在你的 Spring Boot 项目里:
- 加依赖:
spring-cloud-starter-alibaba-seata。 - 改配置 (
application.yml):
seata:
registry:
type: nacos #查找TC服务,参考registry.conf
nacos:
server-addr: 192.168.204.129:8848
namespace: ""
group: SEATA_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
知识点:Seata 的 AT 模式原理(它是咋工作的?)
Seata 最常用的是 AT 模式,主打一个“无侵入”(代码改动极小)。
第一阶段(Try):
- 业务 SQL 执行。
- Seata 拦截 SQL,生成
undo_log(记录修改前的数据和修改后的数据)。 - 本地事务提交(注意:这里就提交了,所以性能高,不锁表太久)。
- 向 TC 汇报:“老板,我这步做完了,但我还没最终确认。”
第二阶段(Confirm/Rollback):
- 如果大家都成功:TM 通知 TC 提交,TC 异步删除
undo_log,完事。 - 如果有人失败:TM 通知 TC 回滚,TC 让 RM 根据
undo_log生成反向 SQL(比如把UPDATE变成REVERSE UPDATE),把数据恢复回去。
总结一句话:
Seata 就是帮你自动记录“后悔药”(undo_log),出事了自动喂给你吃,让你回到过去。
更多推荐




所有评论(0)