Seata 的独特之处在于它专门为了解决微服务环境下的分布式事务问题而设计,提供了多种灵活的事务模式(AT、TCC、Saga),并且尽量减少对现有业务逻辑的侵入性。与此同时,Seata 努力优化性能,降低锁定和日志开销,从而成为现代微服务架构中分布式事务管理的一个强有力的选择。
业务场景
1. 电子商务订单处理
-
场景描述:当用户下单时,系统需要创建订单、扣减库存、生成账单,并可能还需要更新用户的积分或优惠券状态。这些操作涉及多个独立的服务,如订单服务、库存服务、账单服务和用户服务。
-
Seata 应用:通过 Seata 的全局事务管理,可以确保所有这些操作要么全部成功提交,要么全部回滚,从而保持数据的一致性。如果任何一个步骤失败(例如库存不足),整个交易将被取消,避免了部分执行导致的数据不一致。
2. 金融交易
-
场景描述:转账、支付等金融操作通常涉及到资金从一个账户转移到另一个账户,同时可能还需要记录交易日志、更新余额以及发送通知给相关方。
-
Seata 应用:Seata 可以帮助确保跨多个账户和服务的资金转移操作是原子性的。即使在网络故障或其他异常情况下,也能保证资金的安全性和准确性,防止出现“钱去哪了”的问题。
3. 供应链管理
-
场景描述:供应链中的采购订单处理包括确认供应商报价、安排运输、接收货物入库等一系列复杂的流程。每个环节都可能由不同的系统负责,并且需要确保信息同步。
-
Seata 应用:使用 Seata 来协调不同服务之间的交互,保证从下单到收货的全过程都在同一个全局事务中完成。这样可以减少人为干预,提高效率,并确保各环节的信息一致性。
4. 在线票务预订
-
场景描述:电影票、火车票、飞机票等在线预订系统要求快速响应用户的购买请求,同时要精确地控制座位或舱位的可用性。
-
Seata 应用:Seata 能够有效地管理高并发环境下的票务锁定和解锁操作,确保每位顾客都能准确获得他们想要的座位,而不会因为并发冲突而导致超售或误售的情况发生。
5. 物流配送调度
-
场景描述:物流公司需要根据客户订单来规划运输路线、分配车辆资源、安排司机工作,并跟踪包裹的状态直到最终送达目的地。
-
Seata 应用:通过 Seata 组织起一系列相关的业务活动,形成一个完整的配送任务链条。无论哪个环节出现问题,都可以及时回滚变更,重新安排新的方案,确保整个物流过程顺畅无误。
6. 会员积分兑换
-
场景描述:许多电商平台允许用户使用积分兑换商品或服务。这个过程中涉及到积分扣除、商品发放等多个步骤,必须确保每一步都能够正确执行。
-
Seata 应用:借助 Seata 实现积分兑换的全局事务管理,保障积分扣除与商品发放之间的一致性。即使在复杂促销活动期间,也能维持系统的稳定运行,提供良好的用户体验。
7. 保险理赔
-
场景描述:保险公司处理理赔申请时,往往需要审核材料、评估损失、计算赔付金额、转账支付等多个步骤,而且这些步骤可能分散在不同的部门或系统中。
-
Seata 应用:Seata 可以将理赔流程中的各个阶段封装成一个全局事务,确保所有操作都按照预定规则顺利完成。一旦某个环节出错,则整个流程会自动回滚,保护客户的权益不受损害。
8. 社交网络内容发布
-
场景描述:社交媒体平台上的内容发布可能涉及上传图片/视频、保存文本、标签关联、通知粉丝等多个子任务。为了保证用户体验的一致性,这些操作需要作为一个整体来处理。
-
Seata 应用:利用 Seata 来协调这些子任务之间的关系,确保用户看到的内容是完整且一致的。如果其中任何一部分失败,比如图片上传失败,那么整个发布过程都会被撤销,避免了残缺不全的信息展示给用户。
Seata与其他事务管理方案的区别
1. 与 XA 协议的区别
-
XA协议:这是一个传统的两阶段提交(2PC)协议,广泛应用于关系型数据库中以支持分布式事务。它要求所有参与事务的资源管理器都必须支持 XA 接口,并且整个过程涉及大量的锁和日志记录,可能导致性能瓶颈。
-
Seata:
-
优化性能:Seata 的 AT 模式通过本地事务实现分布式事务的效果,避免了传统 XA 的缺点,减少了对应用层的侵入性,同时提高了性能。 -
简化开发:Seata 提供更简单的 API 和注解支持,让开发者更容易地实现分布式事务,而不需要深入理解复杂的 2PC 协议。
2. 与 TCC 模式的区别
-
TCC模式:Try-Confirm-Cancel 是一种显式的分布式事务模型,开发者需要为每个业务操作定义三个方法:尝试(Try)、确认(Confirm)和取消(Cancel)。这种方式提供了较强的控制力,但增加了开发复杂度。
-
Seata:
-
多样化选择:Seata 支持 TCC 模式,但也提供了更简单的 AT 模式作为默认选择,AT 模式对于大多数场景来说已经足够强大,而且对业务逻辑的影响较小。 -
内置补偿机制:Seata 还引入了 Saga 模式,适用于长流程编排的场景,使得开发者可以更容易地构建基于补偿的分布式事务。
3. 与消息驱动事务(如 MQ + 最终一致性)的区别
-
消息驱动事务:利用消息队列实现最终一致性,即通过发送消息触发后续操作,确保即使某些操作暂时失败也能在稍后重试成功。这种方式依赖于消息中间件的可靠性和延迟容忍度。
-
Seata:
-
强一致性保障:Seata 提供了更为严格的一致性保障,尤其是在强一致性要求较高的场景下。虽然它可以结合消息队列使用,但 Seata 本身并不依赖于特定的消息中间件,而是专注于协调多个服务之间的事务。 -
减少延迟:与最终一致性不同,Seata 力求尽可能快地完成全局事务的提交或回滚,减少业务操作的延迟。
4. 与 Spring Cloud Sleuth 和 Zipkin 等追踪工具的区别
-
Spring Cloud Sleuth 和 Zipkin:这些工具主要用于分布式系统的追踪,它们可以帮助理解和分析请求在不同服务间的流动路径,但对于事务管理和一致性保证没有直接的帮助。
-
Seata:
-
专注一致性:相比之下,Seata 主要关注分布式事务的一致性问题,尽管它也可以与追踪工具集成以提供更好的可观测性。 -
全局事务管理:Seata 提供了一套完整的分布式事务管理解决方案,包括事务的开始、提交、回滚等操作,以及分支事务的注册和管理。
5. 与 Saga 模式(非 Seata 内置)的区别
-
Saga模式:这是一种用于处理长时间运行事务的模式,通过一系列步骤组成一个工作流,每一步都有相应的补偿操作。如果某一步失败,则回滚到前一步并执行其补偿动作,直到回到初始状态。
-
Seata:
-
内置支持:Seata 内置了 Saga 模式的支持,使得开发者可以更容易地构建基于补偿的分布式事务。Seata 的 Saga 模式不仅限于 RESTful API 调用,还可以应用于任何类型的服务调用,并且提供图形化界面帮助设计和管理复杂的业务流程。 -
集成优势:Seata 的 Saga 模式与其他组件(如 AT 和 TCC)无缝集成,允许根据业务需求灵活切换不同的事务管理模式。
6. 与 Compensation-based transactions(基于补偿的事务)的区别
-
基于补偿的事务:这种模式通常用一系列步骤组成一个工作流,每一步都有相应的补偿操作。如果某一步失败,则回滚到前一步并执行其补偿动作,直到回到初始状态。
-
Seata:
-
内置支持:Seata 的 Saga 模式本质上是基于补偿的事务,但它提供了更加结构化的框架和支持,简化了补偿逻辑的实现。 -
统一管理:Seata 不仅支持基于补偿的事务,还提供了全局事务的统一管理和监控能力,使得开发者可以更好地掌控整个事务生命周期。
代码实操
步骤 1: 设置 Seata Server
首先,确保你已经安装并启动了 Seata Server。你可以按照官方文档的指导使用 Docker 或者直接下载二进制文件进行部署。
步骤 2: 修改 pom.xml
在你的 Spring Boot 项目中添加 Seata 相关依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
步骤 3: 配置 application.yml
配置 Seata 客户端连接到 Seata Server:
spring:
datasource:
url:jdbc:mysql://localhost:3306/db_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username:root
password:root
driver-class-name:com.mysql.cj.jdbc.Driver
cloud:
alibaba:
seata:
tx-service-group:my_test_tx_group# 全局事务组名
seata:
enabled:true
config:
type:"file"
service:
vgroup-mapping:
my_test_tx_group:default# 映射全局事务组到Seata服务组
grouplist:
default:"127.0.0.1:8091"# Seata Server地址
步骤 4: 创建 Service 类
OrderService.java
@Service
publicclass OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageService storageService;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(Order order) {
// 创建订单
orderMapper.create(order);
// 扣减库存
storageService.decrease(order.getProductId(), order.getCount());
// 模拟业务逻辑失败
if (order.getCount() > 10) {
thrownew RuntimeException("模拟异常");
}
}
}
StorageService.java
@Service
public class StorageService {
@Autowired
private StorageMapper storageMapper;
public void decrease(Long productId, Integer count) {
storageMapper.decrease(productId, count);
}
}
步骤 5: 创建 Mapper 接口
OrderMapper.java
@Mapper
public interface OrderMapper {
@Insert("INSERT INTO t_order (user_id, product_id, count, money) VALUES (#{userId}, #{productId}, #{count}, #{money})")
void create(Order order);
}
StorageMapper.java
@Mapper
public interface StorageMapper {
@Update("UPDATE t_storage SET count = count - #{count} WHERE product_id = #{productId}")
void decrease(@Param("productId") Long productId, @Param("count") Integer count);
}
步骤 6: 创建实体类
Order.java
@Data
public class Order {
private Long id;
private Long userId;
private Long productId;
private Integer count;
private BigDecimal money;
}
测试
我用的是MySQL数据库,创建了两个表:t_order
和 t_storage
。
t_order 表结构
t_order
表用于存储订单信息。每个订单包含用户 ID、产品 ID、购买数量以及订单金额等信息。
CREATE TABLE t_order (
idBIGINT AUTO_INCREMENT PRIMARY KEY, -- 订单ID,主键
user_id BIGINTNOTNULL, -- 用户ID
product_id BIGINTNOTNULL, -- 产品ID
countINTNOTNULL, -- 购买数量
money DECIMAL(10, 2) NOTNULL, -- 订单金额
create_time TIMESTAMPDEFAULTCURRENT_TIMESTAMP, -- 创建时间
update_time TIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP-- 更新时间
);
t_storage 表结构
t_storage
表用于管理产品的库存信息。它记录了每种产品的当前库存数量。
CREATE TABLE t_storage (
idBIGINT AUTO_INCREMENT PRIMARY KEY, -- 库存ID,主键
product_id BIGINTNOTNULLUNIQUE, -- 产品ID,唯一标识
countINTNOTNULL, -- 库存数量
create_time TIMESTAMPDEFAULTCURRENT_TIMESTAMP, -- 创建时间
update_time TIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP-- 更新时间
);
前面一切正常,订单将被创建并且相应的产品库存会被扣减。
为了我的测试,我设置 order.getCount()
大于 10,则整个事务会回滚,既不会创建订单也不会扣减库存。
注意事项
-
Seata Server:确保 Seata Server 已经正确启动,并且客户端能够连接。 -
数据源配置:每个参与分布式事务的服务都需要正确配置自己的数据源,并且这些数据源应当是经过 Seata 包装过的,以便它可以拦截 SQL 并管理分支事务。 -
网络和防火墙设置:确保所有服务之间的网络通信畅通无阻,包括与 Seata Server 的连接。
关注我,送Java福利
/**
* 这段代码只有Java开发者才能看得懂!
* 关注我微信公众号之后,
* 发送:"666",
* 即可获得一本由Java大神一手面试经验诚意出品
* 《Java开发者面试百宝书》Pdf电子书
* 福利截止日期为2025年01月30日止
* 手快有手慢没!!!
*/
System.out.println("请关注我的微信公众号:");
System.out.println("Java知识日历");
原文始发于微信公众号(Java知识日历):SpringBoot整合Seata,解决多个订单相关服务之间数据一致性问题
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/310622.html