禅与计算机 禅与计算机
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
      • 理论篇
        • 什么是事务
      • 分布式事务简介
        • CAP原则
        • 什么是BASE理论
      • 什么是Seata
      • Seata的三大角色
      • 实践篇
        • 部署Seata并注册到Nacos上
        • 在服务中集成Seata
      • 基于Seata实现分布式事务的四种方式
        • XA模式
        • 了解XA模式的工作原理和优缺点
        • 实践
        • AT模式
        • AT模式优缺点以及和XA模式的区别
        • TCC模式
        • SAGA模式
        • 总结优缺点
      • 分布式事务使用seata处理呢?为什么不用mq解决分布式事务的问题呢
      • 参考文献
    • 一步一步教你:用 Docker Compose 完成 Seata 的整合部署
  • Nacos

  • Seata

  • Docker部署

  • 分布式微服务
  • 微服务基础
sharkchili
2023-01-07
目录

分布式事务Seata实践

# 理论篇

# 什么是事务

关于事务我们一定会想到下面这四大特性:

  1. 原子性:所有操作要么全都完成,要么全都失败。
  2. 一致性: 保证数据库中的完整性约束和声明性约束。
  3. 隔离性:对统一资源的操作不会同时发生的。
  4. 持久性:对事务完成的操作最终会持久化到数据库中。

# 分布式事务简介

而分布式事务,不仅包含上述四个特性,这个事务可能是跨服务也可能是跨数据源的一种事务。

# CAP原则

CAP我们可以将其拆分为3个字母,对应中文含义分别是:

  1. C(Consistency):一致性。使用相同条件到集群中的节点获取数据都是一致的,当我们将任意节点提交修改,其他节点也会完成数据同步。

在这里插入图片描述 2. A(Availability):可用性。用户访问集群中任意一个健康节点都会得到响应,而不是像报错或者响应超时。 3. P(Partition tolerance):容错性。集群中的节点可能会因为各种原因和集群失去联系(例如当前节点和集群通信的端口号被运维不小心关闭了),但是这些节点还是可以正常对外提供服务,这种分区了还能对外部提供的服务的特性就叫做分区容错性。

在这里插入图片描述

为什么CAP在符合分区容错性(P)的基础上只能在一致性(C)和可用性(A)中选择一个特性呢?

由上我们可以知道P肯定是可以满足的,如果我们需要满足C(一致性),那么集群断开连接时,我们必须等待各个节点再次连接并完成数据同步后才能对外提供服务,那么在一定时间内,服务是不可用的。如果我们要满足A(可用性),那么一旦集群节点断开连接后不管数据是否同步都要对外提供服务,这样一来C(一致性)就不一定可以满足。

这就是为什么CAP只能符合CP或者AP。

# 什么是BASE理论

而CAP一种解决理论则是BASE理论,即:

  1. Basically Available (基本可用):允许分布式事务运行过程中损失部分可用性,即保证核心可用。
  2. Soft State(软状态):在数据更新时,允许某一小段时间数据不一致。
  3. Eventually Consistent(最终一致性):虽然无法保证强一致性,但是软状态结束后,还是可以保证最终数据是一致性的。

# 什么是Seata

Seata是一款开源的分布式事务解决方案。Seata这个开源工具实现分布式事务则是基于以下三个角色:

# Seata的三大角色

  1. TC (Transaction Coordinator)事务协调者 :维护全局事务和分支事务的状态,协调全局事务的提交和回滚。
  2. TM (Transaction Manager) 事务管理器:定义全局事务的范围,开始全局事务、提交或者回滚全局事务。
  3. RM (Resource Manager) 资源管理器:管理分支事务处理的资源,并向TC注册分支事务以及报告分支事务的状态,并驱动分支事务的提交和回滚。

上述的概念可能读者不态度,我们不妨以下图为例子讲述一下这三大角色的工作流程,当我们创建的一个分布式全局事务时,TM就划定了这个事务的范围,见下图蓝色部分。 然后所有的RM都会执行本地事务,所谓本地事务我们可以通俗的理解为JDBC操作SQL,在此期间Seata会记录sql修改前后的镜像到一张名为undo-log的表中,当所有RM都提交成功之后,TM就会驱动TC发起全局事务提交,反之发起全局事务回滚。

在这里插入图片描述

# 实践篇

# 部署Seata并注册到Nacos上

首先我们来部署以下Seata,首先我们得去官网下载一下资源,以笔者为例,笔者当前使用的是1.4.2这个版本:

https://github.com/seata/seata/releases (opens new window)

在这里插入图片描述

完成后我们就可以开始Seata的配置工作了,首先我们先配置conf目录下的registry.conf,读者可以参见以下注释自行修改。这里需要留意一下笔者下面配置中cluster指定名称为SH,这个配置后续服务注册到Seata中会用到。

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    # 服务注册后的名称
    application = "seata-server"
    # nacos地址
    serverAddr = "127.0.0.1:8848"
    # nacos组名
    group = "myGroup"
    # nacos使用命名空间
    namespace = "dev"
    # 使用集群的节点名称
    cluster = "SH"
    # nacos账户和密码
    username = "nacos"
    password = "nacos"
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

注意为了让tc的集群可以共享配置,我们可以将服务端配置内容配置到nacos上。如下图所示

在这里插入图片描述

可以看到笔者在dev命名空间的myGroup下,添加了下面这样一段配置,读者只需按照下面的模板修改数据库相关的配置即可

# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver

# 读者只需修改下面这些配置即可
store.db.url=数据库地址
store.db.user=用户名
store.db.password=密码



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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

Seata管理全局事务和分支事务也是采用SQL的形式,所以我们需要使用下面这段SQL脚本建立全局事务和分支事务数据表。


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- 分支事务表
-- ----------------------------
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table`  (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `status` tinyint(4) NULL DEFAULT NULL,
  `client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime(6) NULL DEFAULT NULL,
  `gmt_modified` datetime(6) NULL DEFAULT NULL,
  PRIMARY KEY (`branch_id`) USING BTREE,
  INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- 全局事务表
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table`  (
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `timeout` int(11) NULL DEFAULT NULL,
  `begin_time` bigint(20) NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`xid`) USING BTREE,
  INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
  INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

完成这些步骤直接启动Seata即可,以笔者为例,因为笔者用的是windows系统,所以运行bin目录下的seata-server.bat。

若显示出下面这样一段输出,则说明启动Seata成功了

在这里插入图片描述

# 在服务中集成Seata

首先在pom中引入下面这段依赖,可以看到由于版本的原因,笔者手动讲spring-cloud-starter-alibaba-seata中自带的seata-starter排除,手动引入了1.4.2版本的seata。


        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <!--版本较低,1.3.0,因此排除-->
                <exclusion>
                    <artifactId>seata-spring-boot-starter</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--版本在父工程中配置,seata starter 采用1.4.2版本-->
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.4.2</version>
        </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

然后在application.yml中添加下面这样一段配置,具体含义参加注释

seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    type: nacos # 注册中心类型 nacos
    nacos:
      server-addr: 127.0.0.1:8848 # nacos地址
      namespace: "dev" # namespace,默认为空
      group: myGroup # 分组,默认是DEFAULT_GROUP
      application: seata-server # seata服务名称
      username: nacos
      password: nacos
  tx-service-group: seata-demo # 事务组名称
  service:
    vgroup-mapping: # 事务组与cluster的映射关系
      seata-demo: SH
  data-source-proxy-mode: XA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在这里插入图片描述

每个服务都完成上述配置之后,启动时就会用到Seata了。

# 基于Seata实现分布式事务的四种方式

# XA模式

# 了解XA模式的工作原理和优缺点

如下图,XA模式的分布式事务执行流程大抵如下,简而言之,我们可以将其分为两个阶段,首先是:

RM第一阶段:所有的分支事务向TC注册自己的状态,完成后各自执行SQL但是不提交,并将状态报告给TC。

TC 第二阶段:

  1. TC查看当前事务的所有分支事务是否都成功了,如果都成功则协通知所有RM提交事务,反之回滚事务。
  2. 收到TC全局事务提交后,RM将自己管理的分支事务也提交了。

在这里插入图片描述

XA模式的分布式事务优缺点:

  1. 优点: 强一致性,符合ACID原则。 且实现简单,没有代码侵入。
  2. 缺点:为了保证强一致性,所以必须保证所有SQL执行没有问题才能提交,所以一阶段这些数据会被锁住,导致其他需要执行这些SQL的事务被阻塞,性能较差。

# 实践

注意为了保证后续执行不报错,建议将pom中的MySQL驱动版本改为8.0.11

首先修改每个需要使用XA模式的服务,在yml添加下面这样一段配置

seata:
  data-source-proxy-mode: XA
1
2

XA模式代码如下所示,可以看到笔者仅仅是在方法上加一个GlobalTransactional注解就能保证服务1和服务2之间分布式事务的ACID。感兴趣的读者可以自行编写一个demo,可以看到一旦任意服务报错,控制台就会输出RollBack将事务操作回滚。

@GetMapping("/test")
    @GlobalTransactional
    public String hello() {
        UserDto userDto=new UserDto();
        userDto.setLoginName("admin123");
        userDto.setName("admin123");
        userDto.setPassword("456");
        userDto.setImageCode("456");
        userDto.setImageCodeToken("456");
		// 服务1的保存
        userService.save(userDto);
        //服务2的数据操作
        fileService.hello();
        return "123";
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# AT模式

AT模式的工作流程图如下所示,总结以下它的工作流程:

RM第一阶段工作

  1. 注册分支事务。
  2. 解析SQL记录修改前后的SQL镜像并存储到undo-log。
  3. 执行业务SQL并直接提交事务。
  4. 通知TC当前事务的状态。

RM第二阶段工作

  1. 如果TC确定所有事务都成功且发起通知告知当前RM,则RM会将undo-log删除。
  2. 如果TC通知失败则RM会根据undo-log将数据还原。

在这里插入图片描述

# AT模式优缺点以及和XA模式的区别

先来说说和XA模式的区别吧:

  1. XA一阶段会锁定资源,AT模式则是直接提交事务不锁定资源。
  2. XA模式回滚依赖数据库事务,AT模式则是根据我们上面自己创建的undo-log的内容进行还原。
  3. XA模式是强一致性,AT模式是最终一致性。

AT模式优缺点:

  1. 一阶段提交不会锁定资源,性能较好。
  2. 利用全局锁实现读写隔离。
  3. 没有代码侵入,便于使用。

缺点:

  1. 框架记录undo-log会有一定开销,但是性能相比XA会好很多。
  2. 两阶段属于软阶段,属于最终一致性,中间可能会有数据不一致问题。

演示,实现AT模式的方法也很简单,首先为了保证线程安全问题,我们需要对seata数据刷入下面这张表,用于全局锁记录。

-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table`  (
  `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `branch_id` bigint(20) NOT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`row_key`) USING BTREE,
  INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

完成后,对着我们服务需要用到的数据库,刷入undo-log表

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci 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 INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

完成后对着yml文件添加下面这样一段配置

seata:
  data-source-proxy-mode: AT # 默认就是AT
1
2

笔者代码如下所示,可以看到服务1进行保存操作,服务进行其他数据库操作,但是笔者在服务2中随意添加了一个报错。

@GetMapping("/test")
    @GlobalTransactional
    public String hello() {
        UserDto userDto=new UserDto();
        userDto.setId("1");
        userDto.setLoginName("1");
        userDto.setName("1");
        userDto.setPassword("1");
        userDto.setImageCode("1");
        userDto.setImageCodeToken("1");
//服务1的保存
        userService.save(userDto);
//        服务2随意添加了一个报错
        fileService.hello();
        return "123";
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

经过debug我们可以发现,在服务1完成数据提交后

在这里插入图片描述

lock_table记录了一条锁的信息。

在这里插入图片描述

undo-log也会记录一条信息,我们不妨点入查看详细内容

在这里插入图片描述

查看详细内容可以看到,这里面记录的就是插入前后的数据库的镜像

{
    "@class":"io.seata.rm.datasource.undo.BranchUndoLog",
    "xid":"192.168.237.1:8091:6989933091681009799",
    "branchId":6989933091681009804,
    "sqlUndoLogs":[
        "java.util.ArrayList",
        [
            {
                "@class":"io.seata.rm.datasource.undo.SQLUndoLog",
                "sqlType":"INSERT",
                "tableName":"user",
                "beforeImage":{
                    "@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords",
                    "tableName":"user",
                    "rows":[
                        "java.util.ArrayList",
                        [

                        ]
                    ]
                },
                "afterImage":{
                    "@class":"io.seata.rm.datasource.sql.struct.TableRecords",
                    "tableName":"user",
                    "rows":[
                        "java.util.ArrayList",
                        [
                            {
                                "@class":"io.seata.rm.datasource.sql.struct.Row",
                                "fields":[
                                    "java.util.ArrayList",
                                    [
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"id",
                                            "keyType":"PRIMARY_KEY",
                                            "type":1,
                                            "value":"1"
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"login_name",
                                            "keyType":"NULL",
                                            "type":12,
                                            "value":"1"
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"name",
                                            "keyType":"NULL",
                                            "type":12,
                                            "value":"1"
                                        },
                                        {
                                            "@class":"io.seata.rm.datasource.sql.struct.Field",
                                            "name":"password",
                                            "keyType":"NULL",
                                            "type":1,
                                            "value":"1"
                                        }
                                    ]
                                ]
                            }
                        ]
                    ]
                }
            }
        ]
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

一旦报错控制台就会输出一段Rollback的内容,并且事务也会根据undo-log回滚

# TCC模式

TCC模式依旧延续之前的架构,只不过TCC各个阶段都需要人工实现,TCC实现分布式事务我们必须实现以下三个方法:

  1. try:进行资源检测和预留,例如我们要将user1的name从1改为2,那么我们就得将1这个值预留下来(存到一张资源预留表中),并提交1改为2这个操作,并提交事务。
  2. confirm:完成资源的业务操作(将预留表中的内容删除),try成功就要求confirm方法逻辑一定要成功。
  3. cancel:事务提失败,将预留资源释放(将预留表中的数据状态设置为取消,并拿着这个旧值去还原数据,即手动回滚补偿)。

在这里插入图片描述

TCC模式优缺点:

  1. 一阶段即可完成数据提交,释放数据库资源,性能好。
  2. 无需像AT模式那样需要生成快照,使用全局锁,性能最强。
  3. 不依赖数据库事务,而是手动补偿依赖操作,可以用非事务型数据库。

缺点:

  1. 有代码侵入,需要手写资源消耗、补偿等逻辑。
  2. 软状态,属于最终一致性。
  3. 需要考虑confirm和cancel的情况,做好幂等相关处理。

为了演示TCC模式,笔者就基于自己的服务演示一下TCC的使用,举个例子,笔者现在有个为system的服务,system服务会将id为1的user名字进行修改,完成后同步修改file服务的相关数据库。

在这里插入图片描述

所以笔者就以system为例实现一下TCC相关的服务,我们首先梳理一下思路,首先我们要做的就是将id为1的user的name由1改为2。那么我的try方法就需要进行以下操作:

		1. 将user表id为1的用户的旧值冻结起来(这条数据状态设置为0,代表处于try状态),便于后续回滚补偿。
		2. 将user表id为1的用户name值更新。

完成后编写confirm方法,因为只有try成功了才会走到confirm方法,它的逻辑很简单,将记录user表冻结的值的数据删除即可。

而cancel方法就是对资源的补偿处理,框架走到cancel则说明事务执行失败了,我们需要将修改的数据进行补偿,例如我们的user表id为1的name由1改为2,那么我们就需要到资源冻结表找到这个条数据的冻结记录,拿着旧值还原user数据,完成后再将这张资源冻结表数据状态设置为3(已取消)

完成上述逻辑后,我们还需要考虑两个问题,第一个是空悬挂问题,如下图,我们的try方法执行太久导致超时,框架自动执行cancel回滚补偿事务,结果try方法再次执行,因为此时资源已经回滚所以也没有try的必要了。 对此我们的要在try方法加上这么一个逻辑,如果资源冻结表关于本次操作的数据状态为3,则直接返回失败。

在这里插入图片描述

还有一个就是空回滚问题,即try方法执行过程中直接报错或者长时间阻塞了还没执行到sql逻辑,cancel就已经完成回滚了,结果还是走到了cancel方法,cancel并没有需要回滚的数据。 对此,我们只需要将修改前的值手动存到资源冻结表中并设置状态为已取消,制造一条资源冻结数据直接返回即可。

在这里插入图片描述

完成上述分析后,我们就可以进行编码操作了,我们首先需要编写一个@LocalTCC接口,定义try、confirm、cancel方法,可以看到我们用TwoPhaseBusinessAction注解告知框架三大行为用哪个方法,并用BusinessActionContextParameter设置全局参数,confirm和cancel都可以通过BusinessActionContext 获取到这些参数

@LocalTCC
public interface SystemTCCService {


    @TwoPhaseBusinessAction(name = "doTry", commitMethod = "confirm", rollbackMethod = "cancel")
    void doTry(@BusinessActionContextParameter(paramName = "id") String id,
               @BusinessActionContextParameter(paramName = "oldVal") String oldVal,
                @BusinessActionContextParameter(paramName = "val") String val);

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

完成后,我们继承这个类实现一个service,代码如下可以看到笔者doTry做的就是拿着上下文的oldVal(即user表的旧值)冻结起来存到资源冻结表,并将状态设置为0(代表这条数据处于try状态)。通过xid查询资源冻结表看看是否有数据,若有则说明这是一个空悬挂操作,直接返回。 confirm和cancel逻辑就比较简单了,读者可以自行阅读代码和注释,无非是删除补偿数据或回滚数据并作废资源冻结表数据而已。

@Service
public class SystemTCCServiceImpl implements SystemTCCService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private SystemFreezeTblMapper systemFreezeTblMapper;

    @Override
    public void doTry(String id, String oldVal,String val) {

        // 0.获取事务id
        String xid = RootContext.getXID();
        // 业务悬挂判断: 判断freeze中是否有冻结记录,如果有,一定是cancel执行过,拒绝业务操作
        if (systemFreezeTblMapper.selectByPrimaryKey(xid) != null) {
            // cancel执行过,我要拒绝业务
            return;
        }


        User user = new User();
        user.setId(id);
        user.setLoginName(val);
        user.setName(val);
        user.setPassword(val);
        userMapper.updateByPrimaryKeySelective(user);

        //冻结住原有的值
        SystemFreezeTbl systemFreezeTbl = new SystemFreezeTbl();
        systemFreezeTbl.setXid(xid);
        systemFreezeTbl.setUserId(user.getId());
        systemFreezeTbl.setFreezeVal(user.getName());
        systemFreezeTbl.setFreezeOldVal(oldVal);
        //0 try 1 confim 2 cancel
        systemFreezeTbl.setState(0);
        systemFreezeTblMapper.insert(systemFreezeTbl);

    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //成功则删除冻结的表
        String xid = ctx.getXid();
        int count = systemFreezeTblMapper.deleteByPrimaryKey(xid);
        return count > 0;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        // 0.查询冻结记录
        String xid = ctx.getXid();
        // 根据id查询冻结表的记录
        SystemFreezeTbl freeze = systemFreezeTblMapper.selectByPrimaryKey(xid);
        // 处理空回滚
        if (freeze == null) {
            //空回滚
            freeze = new SystemFreezeTbl();
            String userId = ctx.getActionContext("id").toString();
            freeze.setUserId(userId);
            freeze.setFreezeVal(ctx.getActionContext("val").toString());
            freeze.setFreezeOldVal(ctx.getActionContext("oldVal").toString());
            freeze.setState(2);
            freeze.setXid(xid);
            systemFreezeTblMapper.insert(freeze);
            return true;
        }
        // 幂等判断
        if (freeze.getState() == 2) {
            // 已经处理过了cancel,无需重复
            return true;
        }


        //手动补偿user表,进行数据还原
        User user = new User();
        user.setId(ctx.getActionContext("id").toString());
        user.setName(freeze.getFreezeOldVal());
        userMapper.updateByPrimaryKeySelective(user);
		
		//将资源冻结表状态设置为2 代表已取消
        freeze.setFreezeVal("");
        freeze.setState(2);
        int count = systemFreezeTblMapper.updateByPrimaryKey(freeze);

        return count > 0;


    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

完成system核心服务层代码编写之后,我们也可以照猫画虎的完成file服务的编写,笔者这里就不多做演示了,直接贴出controller的代码。我们可以开着debug模式试着将让fileService报错,调试时就会发现systemTCCService最终会执行cancel完成数据补偿。

@GetMapping("/test")
    @GlobalTransactional
    public String hello() {

        systemTCCService.doTry("1", "1", "2");
        fileService.hello();
        return "success";
    }
1
2
3
4
5
6
7
8

# SAGA模式

Saga 模式是 Seata 即将开源的长事务解决方案,将由蚂蚁金服主要贡献。

https://seata.io/zh-cn/docs/user/saga.html (opens new window)

总的来说它和TCC差不多,它也是分为两个阶段:

  1. 一阶段将修改提交。
  2. 二阶段则是根据一阶段进行反馈,若是成功则什么都不做,反之进行回滚补偿。

优缺点

优点:

	1. 基于事件驱动实现异步调用,性能较好,吞吐高。
	2. 一阶段直接提交事务,无锁,性能较好。
	3. 无需像TCC一样手动编写不同阶段的方法。

缺点:

 1. 软状态时间不确定,时效性较差。
 2. 无事务隔离,可能出现脏写的情况。

# 总结优缺点

在这里插入图片描述

# 分布式事务使用seata处理呢?为什么不用mq解决分布式事务的问题呢

主要还是考虑兼容性问题,目前主流的消息中间件中rocketMQ支持事务。而且考虑将来可扩展,可能我们还会更换中间件以及兼容多数据库,所以使用seata实现分布式事务是最合适的。 而且消息队列主要作用也并不是用于分布式事务问题,它的主要作用是解耦、异步、削峰。而seata是目前比较主流的分布式事务解决方案。

# 参考文献

微服务事务——Seata: https://juejin.cn/post/7121180233532702734 (opens new window)

深度剖析 Seata TCC 模式(一):https://seata.io/zh-cn/blog/seata-tcc.html (opens new window)

Seata AT模式:https://seata.io/zh-cn/docs/dev/mode/at-mode.html (opens new window)

深入理解Seata流程:https://blog.csdn.net/weixin_39986856/article/details/103630407 (opens new window)

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
微服务组件Gateway核心使用小结
一步一步教你:用 Docker Compose 完成 Seata 的整合部署

← 微服务组件Gateway核心使用小结 一步一步教你:用 Docker Compose 完成 Seata 的整合部署→

最近更新
01
基于EasyExcel实现高效导出
03-25
02
从开源框架中学习那些实用的位运算技巧
03-25
03
浅谈分布式架构设计思想和常见优化手段
03-25
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×