当前位置: 首页 > ds >正文

分布式事务的Java实践

文章目录

    • 概要
    • 整体架构流程
    • 技术名词解释
      • 微服务
      • 分布式事务
      • Seata
    • 技术细节
      • 前提
      • 整体机制
      • 写隔离
      • 理解
    • 基于spingcloud框架的seata导入
      • 部署seata环境
      • Seata AT模式
      • Seata配置文件
      • nacos配置示例
      • maven项目配置,
      • 测试验证
    • 参考

概要

随着市场发展,微服务架构已成为不可或缺的产物,单体架构已无法满足市场需求,比如一个软件公司,财务,考勤,人事管理,招聘面试,项目管理,资材管理等,是需要多个系统共同经营的,那就需要多个不同类型的数据库数据管理。不可避免的出现系统之间的数据传输和一致性保障,分布式事务应运而生。

整体架构流程

  • 这是一个微服务架构图
    架构图
    事务管理这块,除了MySQL自身支持事务外,MongoDB通过主副本集来管理事务,Seata扮演更重要的角色,就是多数据源的事务控制。

技术名词解释

微服务

微服务是一种软件架构风格,将单一应用程序拆分为多个小型、独立的服务。每个服务运行在独立的进程中,通过轻量级通信机制(如HTTP/REST或消息队列)协作,可独立开发、部署和扩展。核心特点包括松耦合、按业务能力划分、技术栈灵活及容错性强。

分布式事务

分布式事务指跨多个服务或数据库的事务操作,需保证所有参与方数据的一致性(ACID特性)。挑战在于网络延迟、节点故障等导致的协调复杂度。常见解决方案包括两阶段提交(2PC)、补偿事务(TCC)、本地消息表及Saga模式。

Seata

Seata(Simple Extensible Autonomous Transaction Architecture)是阿里开源的分布式事务中间件,支持AT(自动补偿)、TCC、Saga和XA模式。其核心模块包括事务协调器(TC)、资源管理器(RM)和应用(TM),通过全局事务ID实现跨服务事务管理,简化了分布式系统的数据一致性保证。

三者关联:微服务架构中,Seata为分布式事务提供标准化解决方案,确保跨服务调用时的数据一致性。

技术细节

这里重点说一下Seata的AT模式

前提

基于支持本地 ACID 事务的关系型数据库。
Java 应用,通过 JDBC 访问数据库。

整体机制

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

  • 二阶段:
    提交异步化,非常快速地完成。
    回滚通过一阶段的回滚日志进行反向补偿。

写隔离

  • 一阶段本地事务提交前,需要确保先拿到 全局锁
  • 拿不到 全局锁 ,不能提交本地事务。
  • 拿到 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

理解

这是官网给的原理,概括来说,就是执行事务之前,在数据库中存一份回滚记录,如果抛异常触发回滚,就从数据库中拿到回滚记录更新回去,若事务提交成功,则删掉,这里面还有全局锁用到全局事务ID在里面保障事务的原子性。

基于spingcloud框架的seata导入

部署seata环境

  • 这里直接用最方便的docker构建容器
docker run -d --restart always --name seata2-server -p 28091:28091 -p 27091:27091 -v /home/seata2/config:/seata-server/resources m.daocloud.io/docker.io/seataio/seata-server:2.0.0

Seata AT模式

  • 需要的maven项目数据库新建如下表
CREATE TABLE `undo_log` (`id` bigint NOT NULL AUTO_INCREMENT,`branch_id` bigint NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3520 DEFAULT CHARSET=utf8mb3;
  • Seata容器建表
CREATE TABLE `branch_table` (`branch_id` bigint NOT NULL,`xid` varchar(128) NOT NULL,`transaction_id` bigint DEFAULT NULL,`resource_group_id` varchar(32) DEFAULT NULL,`resource_id` varchar(256) DEFAULT NULL,`branch_type` varchar(8) DEFAULT NULL,`status` tinyint DEFAULT NULL,`client_id` varchar(64) DEFAULT NULL,`application_data` varchar(2000) DEFAULT NULL,`gmt_create` datetime(6) DEFAULT NULL,`gmt_modified` datetime(6) DEFAULT NULL,PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;CREATE TABLE `distributed_lock` (`lock_key` char(20) NOT NULL,`lock_value` varchar(20) NOT NULL,`expire` bigint DEFAULT NULL,PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;CREATE TABLE `global_table` (`xid` varchar(128) NOT NULL,`transaction_id` bigint DEFAULT NULL,`status` tinyint NOT NULL,`application_id` varchar(32) DEFAULT NULL,`transaction_service_group` varchar(32) DEFAULT NULL,`transaction_name` varchar(128) DEFAULT NULL,`timeout` int DEFAULT NULL,`begin_time` bigint DEFAULT NULL,`application_data` varchar(2000) DEFAULT NULL,`gmt_create` datetime DEFAULT NULL,`gmt_modified` datetime DEFAULT NULL,PRIMARY KEY (`xid`),KEY `idx_status_gmt_modified` (`status`,`gmt_modified`),KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;CREATE TABLE `lock_table` (`row_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`xid` varchar(128) DEFAULT NULL,`transaction_id` bigint DEFAULT NULL,`branch_id` bigint NOT NULL,`resource_id` varchar(256) DEFAULT NULL,`table_name` varchar(100) DEFAULT NULL,`pk` varchar(36) DEFAULT NULL,`status` tinyint NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',`gmt_create` datetime DEFAULT NULL,`gmt_modified` datetime DEFAULT NULL,PRIMARY KEY (`row_key`),KEY `idx_status` (`status`),KEY `idx_branch_id` (`branch_id`),KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Seata配置文件

#  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: 27091spring:application:name: seata-serverlogging:config: classpath:logback-spring.xmlfile:path: ${log.home:${user.home}/logs/seata} #日志路径extend:logstash-appender:destination: 127.0.0.1:4560kafka-appender:bootstrap-servers: 127.0.0.1:9092topic: logback_to_logstash
## 账号密码
console:user:username: seata123456password: seata123456
seata:config:# support: nacos, consul, apollo, zk, etcd3type: nacosnacos:server-addr: 192.168.x.xxx:8848  # nacos的ip端口group: SEATA2_GROUP        # 对应的组,默认为DEFAULT_GROUPusername: nacospassword: nacosdata-id: seataServer.properties # nacos中存放seata的配置文件,后面会提该文件的使用方式,相当于seata服务启动的时候需要注册到nacos,并使用nacos中的配置文件registry:# support: nacos, eureka, redis, zk, consul, etcd3, sofatype: nacosnacos:application: ${spring.application.name}server-addr: 192.168.x.xxx:8848group: SEATA2_GROUPcluster: defaultusername: nacospassword: nacosstore:# support: file 、 db 、 redis 、 raftmode: dbserver:service-port: 28091 #If not configured, the default is '${server.port} + 1000'security:secretKey: SeataSecretKeyxxxxxxxxtokenValidityInMilliseconds: 1800000ignore:urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**

nacos配置示例

# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://xxx
store.db.user=xxx
store.db.password=xxx
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=3000
server.recovery.asynCommittingRetryPeriod=3000
server.recovery.rollbackingRetryPeriod=3000
server.recovery.timeoutRetryPeriod=3000
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

maven项目配置,

  • 多个maven可以共用如下配置
seata:enabled: trueenable-auto-data-source-proxy: false # 多数据源需要设置enable-auto-data-source-proxy为falseapplication-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name}tx-service-group: group1 # Seata 事务组编号,用于 TC 集群名# 服务配置项,对应 ServiceProperties 类service:# 虚拟组和分组的映射vgroup-mapping:group1: default# 分组和 Seata 服务的映射grouplist:default: 192.168.x.xxx:28091

测试验证

@GetMapping("/transactionTest")
@ApiOperation("事务测试")
@Transactional
public Result transactionTest(@RequestParam Integer num, @RequestParam Integer isError){//这里seataTestDao更新的本地MySQL数据库SeataTest seataTest = seataTestDao.selectById(1);seataTest.setNum(seataTest.getNum()+num);seataTestDao.updateById(seataTest);//这里feignService通过feigin调用接口更新另外一个容器数据库SeataTest2 seataTest2 = feignService.getSeataTest2ById(1);seataTest2.setNum(seataTest2.getNum()+num);feignService.updateSeataTest2ById(seataTest2);if(isError == 1){throw new RunTimeException();}return Result.ok();
}
  • 通过测试,查看对应表,验证事务生效。

参考

  • Seate官网AT模式
http://www.xdnf.cn/news/20285.html

相关文章:

  • 面试官问:你如何提高工作效率?
  • 专项智能练习(计算机动画基础)
  • java log相关:Log4J、Log4J2、LogBack,SLF4J
  • 安防芯片ISP白平衡统计数据如何提升场景适应性?
  • 微信小程序如何进行分包处理?
  • 源雀SCRM开源:企微文件防泄密
  • 2025职教技能大赛汽车制造与维修赛道速递-产教融合实战亮剑​
  • 【详细指导】多文档界面(MDI)的应用程序-图像处理
  • 第三方web测评机构:【WEB安全测试中HTTP方法(GET/POST/PUT)的安全风险检测】
  • Unity3D URP线性空间UI透明度混合解决方案
  • linux离线安装elasticsearch8.19.3
  • 3.4_第二行之_ipipe_init_early()
  • 通用虚拟示教器:让机器人教学像玩游戏一样简单
  • 从音频到文本实现高精度离线语音识别
  • 【FastDDS】概述 Library Overview
  • Ubuntu 24.04.2安装k8s 1.33.4 配置cilium
  • finalize() 方法介绍
  • unity 接入火山引擎API,包括即梦AI
  • flutter-使用fluttertoast制作丰富的高颜值toast
  • 从 ETL 到 Agentic AI:工业数据管理变革与 TDengine IDMP 的治理之道
  • Android8 binder源码学习分析笔记(二)
  • Java 操作 Excel 全方位指南:从入门到避坑,基于 Apache POI
  • Spring Boot 源码深度解析:揭秘自动化配置的魔法
  • AR技术:电力巡检的智能升级之路
  • Python的RSS/Atom源解析库feedparser
  • 【微知】vscode如何开启markdown的review模式?
  • 飞算JavaAI炫技赛:在线图书借阅平台的设计与实现
  • 【完整源码+数据集+部署教程】雪崩检测与分类图像分割系统源码和数据集:改进yolo11-HSFPN
  • 网页版的云手机都有哪些优势?
  • C++(Qt)软件调试---bug排查记录(36)