seata 服务搭建

1. 分布事务

        事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列SQL操作,这些 操作作为一个整体一起向系统提交,要么都执行、要么都不执行。

1.1 ACID事务的特点

事务的4个特性(ACID):

1) 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

2) 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。(实例:转账,两个账户余额相加,值不变。)

3) 隔离性(isolation):一个事务的执行不能被其他事务所影响。

4) 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
 

1.2 事务并发带来的问题

1. 更新丢失(Lost Update)
两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
2. 脏读(Dirty Read)
又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。
例如:事务T1修改了一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因Rollback了,那么事务T2读取的数据就是脏的。
3. 不可重复读(Non-Repeatable Read)
是指在一个事务中两次读同一行数据,可是这两次读到的数据不一样。
例如:事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
4. 幻读
事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据
例如:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样。这就叫幻读。

不可重复读重点在于update和delete,而幻读的重点在于insert。所以说不可重复读和幻读最大的区别,就在于如何通过锁机制来解决他们产生的问题。

1.3 我们可以使用事务的隔离级别来解决

以上的4种问题(更新丢失、脏读、不可重复读、幻读)都和事务的隔离级别有关。通过设置事务的隔离级别,可以避免上述问题的发生。

1. 读未提交(Read Uncommitted)
读事务不阻塞其他读事务和写事务,未提交的写事务阻塞其他写事务但不阻塞读事务。
此隔离级别可以防止更新丢失,但不能防止脏读、不可重复读、幻读。
此隔离级别可以通过“排他写锁”实现。

2. 读已提交(Read Committed)
读事务允许其他读事务和写事务,未提交的写事务禁止其他读事务和写事务。
此隔离级别可以防止更新丢失、脏读,但不能防止不可重复读、幻读。
此隔离级别可以通过“瞬间共享读锁”和“排他写锁”实现。

3. 可重复读取(Repeatable Read)
以操作同一行数据为前提,读事务禁止其他写事务但不阻塞读事务,未提交的写事务禁止其他读事务和写事务。
此隔离级别可以防止更新丢失、脏读、不可重复读,但不能防止幻读。
此隔离级别可以通过“共享读锁”和“排他写锁”实现。

4. 序列化(Serializable)
提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。
此隔离级别可以防止更新丢失、脏读、不可重复读、幻读。
如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

2. 分布式事务

2.1 如何解决分布式问题:

(1)借助rabbit MQ 消息 中间件

(2) 可以自己编写代码完成分布式事务

(3) 借助于第三方分布式事务服务器。---阿里巴巴seata---分布式事务 问题

2.2 介绍seata

支持集群模式--集群模式

        Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用 的分布式事务服务。Seata 将为用户提供了 AT[两次提交]、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

          Seata的执行流程如下: 1. A服务【订单微服务】的TM[事务发起者]向TC[seata服务端]申请开 启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID 2. A服务开始远程调用B服务【账户微服务】,此时XID会在微服务的 调用链上传播 3. B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的 管辖 4. B服务执行分支事务,向数据库做操作 5. 全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的 提交或者回滚 6. TC协调其管辖之下的所有分支事务, 决定是否回滚

TM:事务发起者【在哪个方法上添加了全局事务注解的】 

TC : 事务管理器【seata的服务端】

RM: 每个操作数据库的微服务

XID: 全局事务id

2.3 seata  服务端的搭建

(1)下载seata1.3.0--->  https://github.com/seata/seata/releases/tag/v1.3.0

 (2) 解压

(3)修改conf/file.conf

 

(4) 创建数据库并导入表结构

(5)指定seata的注册中心地址和配置中心的内容

 

使用nacos/nacos-fonfig.sh 把配置信息放入nacos配置中心

如果想人为指定nacos配置中心的地址

 

2.4 配置微服务客户端--必须知道

(1)在每个数据库中创建unlog表

 

(2)在每个微服务中添加seata依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>seata_parent</artifactId>
        <groupId>com.ykq</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>seata-order</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.ykq</groupId>
            <artifactId>seata-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--seata 一定要保证和seata服务的版本匹配-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>


        <!--阿里巴巴的注册中心nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--springbootweb项目-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring连接数据库的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--springboot和mybatis整合的依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--openfeign的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--热部署依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!--mysql的驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--为了让你的application.yml文件有提示-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--spring单元测试的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

</project>

(3) 修改配置文件

server:
  port: 8001

spring:
  application:
    name: seata-order
# 数据源
  datasource:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/seata_order?serverTimezone=Asia/Shanghai
      username: root
      password: abc123
# 把该服务注册到nacos上
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alibaba:
      seata:
        tx-service-group: guangzhou

# 配置mybatis映射文件的路径
mybatis:
  mapperLocations: classpath:mapper/*.xml
# 显示mybatis执行的sql语句
logging:
  level:
    com.ykq.dao: debug

#指定seata服务器在nacos的注册中心地址
seata:
  registry:
    type: nacos
    nacos:
      # 指定seata服务器在注册中心的地址
      server-addr: localhost:8848
      username: nacos
      password: nacos
      #指定seata服务器在注册中心的组名 默认SEATA_GROUP
      group: SEATA_GROUP
      # 指定seata服务器在注册中心的服务名称 默认 seata-server
      application: seata-server
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      username: nacos
      password: nacos
      group: SEATA_GROUP

 

注意:MySQL的驱动名称如果为8.0一定要为com.mysql.cj.jdbc.Driver. 如果为5.7一下的可以用com.mysql.jdbc.Driver

在订单接口中@GlobalTransactiona

package com.ykq.service.impl;

import com.ykq.dao.OrderDao;
import com.ykq.entity.Order;
import com.ykq.feign.AccountFeign;
import com.ykq.feign.StorageFeign;
import com.ykq.service.OrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private AccountFeign accountFeign;//表示调用的远程服务。

    @Autowired
    private StorageFeign storageFeign;

    @Override
    //@Transactional
    //分布式事务注解
    @GlobalTransactional
    public void saveOrder(Order order) {
        log.info("-------->开始创建新订单");
        orderDao.saveOrder(order);

        log.info("-------订单微服务开始调用账户,做扣减");
        accountFeign.increase(order.getUserId(),order.getMoney()); //当前的事务回滚只会回滚自己的数据
        log.info("-------订单微服务开始调用账户,做扣减end");

        //int  c=10/0;
        log.info("--------订单微服务开始调用库存,做扣减");
        storageFeign.increase(order.getProductId(),order.getCount());
        log.info("-------订单微服务开始调用库存,做扣减end");


        log.info("-------修改订单状态");
        orderDao.updateStatus(order.getId());

        log.info("-------修改订单状态结束");

        log.info("--------下订单结束了,哈哈哈哈");
    }

}

启动服务: 

访问: 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!关于 Seata 集群的搭建和应用,我可以给您一些基本的指导。 首先,Seata 是一款开源的分布式事务解决方案,它可以帮助应用在分布式环境下维护数据一致性。下面是大致的 Seata 集群搭建和应用的步骤: 1. 下载和安装 Seata:您可以从 Seata 的官方网站下载最新的 Seata 版本,并按照官方文档进行安装。 2. 配置 Seata 服务端:在配置文件中,您需要指定 Seata 服务端的相关参数,比如注册中心的地址、数据库存储方式等。 3. 启动 Seata 服务端:根据官方文档,启动 Seata 服务端程序。 4. 配置应用程序:在您的应用程序中,需要添加 Seata 的相关依赖,并配置 Seata 的代理数据源,以便 Seata 可以截获并管理分布式事务。 5. 使用 Seata 进行分布式事务管理:在应用程序中,可以使用 Seata 提供的注解或 API 进行分布式事务的管理。比如 `@GlobalTransactional` 注解可以标记一个方法为全局事务的入口,Seata 会在该方法执行过程中对分布式事务进行管理。 6. 验证和测试:在集群环境下,通过模拟不同分支的事务操作,测试 Seata 是否能够正确地进行分布式事务管理和数据一致性的维护。 请注意,以上只是一个大致的步骤概述,具体的操作和配置还需要根据您的实际情况来进行调整。同时,Seata 的官方文档提供了详细的安装和使用指南,您可以参考官方文档以获得更多细节和帮助。 希望能对您有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值