seata相关图形,dljd,cat

一 seate分布式事务

1 业务

        分布式事务的产生,是由于数据库的拆分(拆成多个数据库了,即本来数据只存储在一个数据库中,现在分布地存储在多个数据库中)和分布式的应用架构(微服务)带来的。

        常见的分布式事务应用场景有哪些?在常规情况下,我们在一个进程(java程序/项目)中操作一个数据库,这属于本地事务。但如果在一个进程中操作多个数据库,或者在多个进程中操作一个或多个数据库,就产生了分布式事务;

2 需求

  1. 仍然保证分布式事务的ACID、事务隔离级别、事务的传播级别

3 解决方案

(1)功能"集":AT、TCC、SAGA和XA

        Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务;Seata为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案;

(2)"神工具":seata

4 完成学习

(1) 思想、思路

  1. 本地的事务:数据库本地的事务(ACID、事务隔离性、事务传递性)
  2. 分布式事务:如果在一个进程(java程序/项目)中操作多个数据库,或者在多个进程(java程序/项目)中操作一个或多个数据库,就产生了分布式事务;

(2) 体系组织

  1. Alibaba的产品、开源 、AT/TCC/SAGA和XA
    1. TCC事务模式:资金交易相关、比较严谨的事务管理模式、如银行
    2. XA事务模式:正在开发中...,其他事务模式已经实现;
    3. 目前使用的流行度情况是:AT > TCC 或  AT+TCC混合 > Saga;
      1. 列表显示,大部分公司使用的是AT模式。原理是,改良后的两阶段提交方案。
      2. TCC是三阶段提交的方案。
  2. 数据库分库分表就产生了分布式事务;
  3. 项目拆分服务化也产生了分布式事务;

(3) 原理,流程

        在Seata的架构中,一共有三个角色:

TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚;

TM (Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务;

RM (Resource Manager) - 资源管理器

管理分支事务处理的资源(cat:就是管理和操作具体的,各个分支的那些事务),与TC交互以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚;

其中TC为单独部署的 Server 服务端,TM和RM为嵌入到应用中的 Client 客户端;

  1.  Cat:案例1
    1. 比如要对3个数据库进行操作。
    2. TM是整个事务的发启者(人)。
    3. TC是整个事务的协调者(人)。
    4. RM是管理和操作具体的,各个分支的那些事务。

在Seata中,一个分布式事务的生命周期如下:

TM请求TC开启一个全局事务,TC会生成一个XID作为该全局事务的编号,XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起;(ps:即在3个微服务中都可以拿到这个全局事务的编号。而3个微服务中的事务,都是全局事务的子事务(分支事务)。这里RM,相当于一个子事务(分支事务,或称为本地事务)。)

RM请求TC将本地事务注册为全局事务的分支事务,通过全局事务的XID进行关联;

TM请求TC告诉XID对应的全局事务是进行提交还是回滚;

TC驱动RM将XID对应的自己的本地事务进行提交还是回滚;(ps:TC就会把3个微服务中的每个RM提交)

ps:如上图所示:

  1. 有3个微服务(每个Microservices是代表一个微服务)。
  2. 第1个微服务
    1. 有1个TM,事务管理器,事务的发起者。事务开始,即第1 ~ 3个微服务对数据库的所有操作,都要包含在此事务中。要么都提交成功,要么都回滚。 
    2. 有2个RM,即第1个微服务内部也要操作数据库,即也有自己的事务处理。
  3. 第2个微服务(由第1个微服务调用)
    1. 有2个RM,即第1个微服务内部也要操作数据库,即也有自己的事务处理。
  4. 第3个微服务(由第2个微服务调用)
    1. 有2个RM,即第1个微服务内部也要操作数据库,即也有自己的事务处理。
  5. TC去协调所有的事务处理。

5 TC Server运行环境部署(TC,中间件,是单独的服务器)

(1)单机环境:学习或测试。linux。

  1. 下载:必须下二进制binary 
  2. 解压:tar -zxvf seata-server-1.3.0.tar.gz。得seate目录。
    1. bin目录:启动脚本
      1. .bat是window下启动
      2. .sh是linux下启动
    2. conf目录:配置文件
    3. lib目录:jar包
      1. java语言写的。
      2. 包含自己开发的一些jar包。
      3. 还依赖了一些第三方jar包。
    4. LICENSE:许可证
  3. 切换:cd seata

  4. 启动

    1. 注:默认seata-server.sh脚本设置的jvm内存参数2G,我们再虚拟机里面做实验,可以改小一点;

      1. vim /seate/bin/seata-server.sh

    2. 前台启动命令:./seata-server.sh

    3. 后台启动命令:nohup + &号

    4. 端口被占用:

      1. netstat -alnp | grep 8125

      2. kill - 9  1899

    5. 启动内容解析:/home/seate/seata/logs/start.out

      1. 默认端口:8091

    6. 数据持久化:

      1. 修改数据持久化模式:/home/seate/seata/conf/application.yml ->mode: file

      2. file:/home/seate/seata/bin/sessionStore/root.data

        1. 因为我们没有修改任何配置文件,默认情况seata使用的是file模式进行数据持久化,所以可以看到用于持久化的本地文件 root.data

        2. 0kb。因为没有开启事务、没有注册事务、没有注册服务、没有开启事务操作。

    7. 验证:jps -l

(2)集群环境:生产。linux。

  1. 业务:Seata TC Server集群
  2. 需求:避免TC服务器的单点故障,实现高可用。
  3. 解决方案:集群部署TC Server
  4. 搭建
    1. 思想、思路
      1. 多个 Seata TC Server 通过 db 数据库或者redis实现全局事务会话信息的共享;

      2. 每个Seata TC Server注册自己到注册中心上(nacos),应用从注册中心获得Seata TC Server实例。

    2. ​​​​​​​体系组织

      1. 模式如下图所示:
        ​​​

        1. 第一步:我们有一个应用程序,这个应用程序要实现分布式事务。应用程序中在依赖了jar包后,相当于自带了TM和RM。
        2. 第二步:部署2个TC Server,都做为一个微服务注册到注册中心上。2个TC Server共享同一份分布式事务编号、状态、TM和RM数据,数据可以持久化到file、db、redis等。
        3. 第三步:应用程序连接TC Server时。首先,会去注册中心获取注册的TC Server的实例(从而知道TC Server的IP和Port),其次,通过IP和Port去连接具体的TC Server。
    3. 具体搭建步骤
      1. 第一步:安装nacos
      2. 第二步:安装mysql
      3. 第三步:安装2个TC Server​​​​​​​
      4. 第四步:2个TC Server数据库环境的准备,并都连接对应的mysql数据库
        1. 第1步:mysql中初始化 Seata TC Server 的 db 数据库,在 MySQL 中,创建 seata 数据库,并在该库下执行如下SQL脚本:使用seata-1.3.0\script\server\db脚本(网盘有共享)
        2. 第2步:修改 seata/conf/file.conf 配置文件,修改使用 db 数据库,实现 Seata TC Server 的全局事务会话信息的共享;

          (1)mode = "db"

          (2)数据库的连接信息:

                          driverClassName = "com.mysql.cj.jdbc.Driver"

                          url = "jdbc:mysql://39.99.163.122:3306/seata"

                          user = "mysql"

                          password = "UoT1R8[09/VsfXoO5>6YteB"

        3. 第3步:检查数据库驱动与数据库版本是否匹配。

      5. ​​​​​​​​​​​​​​第五步:2个TC Server注册到nacos中

        1. ​​​​​​​修改 seata/conf/registry.conf 配置文件,设置使用 Nacos 注册中心;

          1. type = "nacos"
          2. Nacos连接信息:​​​​​​​
            1. nacos {

                  application = "seata-server"

                  serverAddr = "127.0.0.1:8848"

                  group = "SEATA_GROUP"

                  namespace = ""

                  cluster = "default"

                  username = ""

                  password = ""

              ​​​​​​​​​​​​​​}

      6. ​​​​​​​​​​​​​​​​​​​​​​​​​​​​第六步:启动数据库

      7. 第七步:启动nacos;

      8. 第八步:启动两个 TC Server

        1. ​​​​​​​解决端口冲突问题

          1. ​​​​​​​​​​​​​​​​​​​​​执行 ./seata-server.sh -p 18091 -n 1 命令,启动第一个TC Server;

            -p:Seata TC Server 监听的端口;

            -n:Server node,在多个 TC Server 时,需区分各自节点,用于生成不同区间的 transactionId 事务编号,以免冲突;

            执行 ./seata-server.sh -p 28091 -n 2 命令,启动第二个TC Server;

      9. 第九步:验证:打开Nacos注册中心控制台,可以看到有两个Seata TC Server 实例;

      10. 第十步:应用测试

6 AT模式事务案例1:单体应用多数据源分布式事务

  1. 单体应用:就一个应用。即一个系统,操作两个或多个数据库。在Spring Boot单体项目中,如果使用了多数据源,就需要考虑多个数据源的数据一致性,即产生了分布式事务的问题,我们采用Seata的AT事务模式来解决该分布式事务问题;就相当于我们这个图:
  2. 以电商购物下单为例。如下图所示,是一个springboot项目,操作了3个数据库。

    1. 程序运行流程:

      1. 第一步:有一个订单Controller,即OrderController。用户请求OrderController,发起一个下单操作。

      2. 第二步:OrderController调用OrderService

      3. 第三步:OrderService调用ProductService,减库存。

      4. 第四步:OrderService调用AccountService,减用户余额。

      5. 第五步:真正插入订单记录。

    2. 分布式事务分析:

      1. 事务包含了更新库存数据库、更新账户数据库、更新订单数据库,而且事务内的3个操作要么都成功,要么都失败(全局回滚)。

    3. 案例演示:

      1. 第一步:环境准备。准备数据库(3个)、表(订单表、库存表、账户表、undo_log表)和数据

        1. 注意:其中每个库中都要额外创建一个undo_log表,它是 Seata AT模式必须创建的表,主要用于分支事务的回滚。

          1. ​​​​​​​字段固定。官方文档(官网)有sql语句。

      2. 第二步:开发一个SpringBoot单体应用(即仅仅一个应用程序)

        1. 第1步:pom.xml,添加依赖。web依赖、lombok依赖、mysql依赖、mybatis依赖、seata依赖、动态数据源(作者是mybatisplus开发者。开源。加入此依赖的可以在一个项目中创建多个数据源来连接多个数据库。)、父依赖、编译设置

        2. 第2步:application.properties配置

        3. 第3步:写代码

          1. 第a步:程序入口类。main方法。

          2. 第b步:XxxController

            1. 注入XxxService

          3. 第c步:XxxService。接口和实现类。

            1. @OS注解,选择数据源。

            2. @GlobalTransactional //seata全局事务注解,开启全局事务,然后就有事务了。类似于spring事务注解@Transaction。这个地方的注解,相当于TM,即全局事务的发起者。此注解放在方法上,此方法有全局分布式事务管理。此注解放在类上,此类的所有方法都有全局的分布式事务管理。

              1. 注解​​​​​​​定义了超时时间

              2. 注解定义了什么时候提交,如何提交

              3. 注解定义了什么时候回滚,如何回滚,及异常后回调的方法

              4. 可以修改事务的隔离性

              5. 可以修改方法的事务的传播性

            3. 方法。方法体内容:

              1. 减库存(数据源1)。RM1,资源管理者1。

              2. 减余额(数据源2)。RM2,资源管理者2。

              3. 添加订单记录(数据源3)。RM3,资源管理者3。

          4. 第d步:mybatis的mapper

          5. 第e步:实体类

      3. 第三步:测试。

        1. 启动程序,并访问:http://localhost:8080/order?userId=1&productId=1

      4. 第四步:效果展示

        1. 订单添加、减库存、减余额,3个操作要么都成功,要么都回滚(全局回滚)。

        2. 注:有了seata事务(@GlobalTransactional ​​​​​​​)以后,可以不加spring的事务(@Transaction)

7 AT模式事务案例2:微服务的分布式事务

  1. 微服务的分布式事务:也就是在springcloud alibaba里面引用seata来管理分布式事务。这种模式下,就相当于我们这个图:
    1. 第一步:我们会有3个微服务(订单微服务、产品微服务、账户微服务)。
    2. 第二步:订单微服务中,OrderController调用OrderService。
    3. 第三步:订单微服务中,OrderService发起了一个远程的feign调用,调用的是产品微服务,对产品库存进行更新操作。
    4. 第四步:订单微服务中,OrderService发起了一个远程的feign调用,调用的是账户微服务,对账户余额进行更新操作。
    5. 第五步:前面操作都成功后,订单微服务中的OrderService在订单数据库中插入订单记录。
  2. 案例演示
    1. 第一步:环境准备。准备数据库(3个)、表(订单表、库存表、账户表、undo_log表)和数据
    2. 第二步:程序开发。springboot、springcloud allibaba、feign、seata
      1. 通用项目commons
        1. 普通的maven项目就可以了
        2. 它主要编写的是model类、feign接口
        3. pom.xml中加入lombok依赖、加入feign(open feign)依赖
        4. 包含:订单feign接口、产品feign接口、账户feign接口。订单实体类、产品实体类、账户实体类。
      2. 开发订单微服务应用程序
        1. 第1步:pom.xml。引入依赖,web依赖、nacos依赖(服务注册与发现)、lombok依赖、mysql依赖、mybatis依赖、seata依赖、通用项目Commons(普通的maven项目就可以了,它主要编写的是model类、feign接口)、父依赖、编译设置
        2. 第2步:application.properties。
          1. 第a步:配置端口
          2. 第b步:配置项目名称
          3. 第c步:订单数据库4要素 
          4. 第d步:连接nacos,服务注册与发现
          5. 第e步:seata的相关的配置
          6. 如下图所示:
        3. 第3步:编码
          1. 第a步:程序入口,主类,main方法(运行)
            1. 注解:开启服务的注册与发现
            2. 注解:开启feign(远程)调用功能
          2. 第b步:XxxController
            1. 注入XxxService
          3. 第c步:XxxService
            1. @GlobalTransactional //seata全局事务注解,开启全局事务,然后就有事务了。类似于spring事务注解@Transaction。这个地方的注解,相当于TM,即全局事务的发起者。此注解放在方法上,此方法有全局分布式事务管理。此注解放在类上,此类的所有方法都有全局的分布式事务管理。

              1. 注解​​​​​​​定义了超时时间

              2. 注解定义了什么时候提交,如何提交

              3. 注解定义了什么时候回滚,如何回滚,及异常后回调的方法

              4. 可以修改事务的隔离性

              5. 可以修改方法的事务的传播性

            2. 方法。方法体内容:

              1. feign远程调用。减库存(数据源1)。RM1,资源管理者1。

              2. feign远程调用。减余额(数据源2)。RM2,资源管理者2。

              3. feign远程调用。添加订单记录(数据源3)。RM3,资源管理者3。

      3. 开发产品微服务应用程序
        1.  基本同上
      4. 开发账户微服务应用程序
        1. 基本同上
    3. 第三步:启动naocs,登录、账户两nacos

    4. 第三步:测试。

      1. 启动3个程序,并访问。

    5. 第四步:效果展示

      1. 订单添加、减库存、减余额,3个操作要么都成功,要么都回滚(不管在哪个地方招聘异常,只要不try/catch,那么都会全局回滚)。

      2. 注:有了seata事务(@GlobalTransactional ​​​​​​​)以后,可以不加spring的事务(@Transaction)

8 AT事务模式分布式事务工作机制

  1. 前提​​​​​​​

    1. 基于支持本地 ACID 事务的关系型数据库;(mysql、oracle)

    2. Java 应用,通过JDBC访问数据库;

  2. 整体机制​​​​​​​:就是两阶段提交协议的演变:

    1. 一阶段:“业务数据“和“回滚日志记录(即undo_log表)“在同一个本地事务中提交,释放本地锁和连接资源;(本地事务,就已经在数据库持久化了)

    2. 二阶段:

      1. 如果没有异常,就异步化提交,非常快速地完成;(正常情况,那就提交了。提交时,同步一下TC Server的状态,删除回滚日志(undo_log表)。)

      2. 如果有异常就回滚,通过一阶段的回滚日志进行反向补偿;(回滚的操作有:比如订单删除,库存加回去,余额加回去)

  3. 具体举例说明整个AT分支的工作过程:

    业务表:product

    Field Type     Key

    id     bigint(20) PRI

    name varchar(100)

    since varchar(100)

    AT分支事务的业务逻辑:

    update product set name = 'GTS' where name = 'TXC';

    一阶段过程:

    1、解析SQL,得到SQL的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息;

    2、查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据;

    select id, name, since from product where name = 'TXC';

    得到前镜像:

    id name since

    1 TXC     2014

    3、执行业务 SQL:更新这条记录的 name 为 'GTS';

    4、查询后镜像:根据前镜像的结果,通过 主键 定位数据;

    select id, name, since from product where id = 1;

    得到后镜像:

    id name since

    1 GTS     2014

    5、插入回滚日志:把前后镜像数据以及业务SQL相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中;

    6、分支事务提交前,向TC注册分支,申请product表中,主键值等于1的记录的全局锁(在当前的同一个全局事务id范围内是可以申请到全局锁的,不同的全局事务id才会排斥);

    7、本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交;

    8、将本地事务提交的结果上报给TC;

    二阶段-回滚

    1、收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作;

    2、通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录;

    3、数据校验:拿 UNDO LOG 中的后镜像与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改,这种情况,需要人工来处理;

    4、根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

    update product set name = 'TXC' where id = 1;

    5、提交本地事务,并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC;

    二阶段-提交

    1、收到TC的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给TC;

    2、异步任务阶段的分支提交请求将异步和批量地删除相应UNDO LOG记录;

    回滚日志表:

    Field         Type

    branch_id     bigint       PK

    xid             varchar(100)

    context         varchar(128)

    rollback_info longblob

    log_status     tinyint

    log_created     datetime

    log_modified datetime

    SQL建表语句:

    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,

      PRIMARY KEY (`id`),

      UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

9 读写隔离:AT事务模式在多线程环境下,会不会存在数据一致性的问题?(可以用JMeter进行测试)

(1)业务:AT事务模式。多线程环境下。数据一致性的问题。

  1. ​​​​​​​此前有,微服务架构下有订单微服务、产品微服务、账户微服务,3个服务都各自操作一个数据库(订单数据库,库存数据库,账户数据库)。

  2. 多线程:如果有多个线程(10个、50个或100个用户)同时都通过订单微服务(去添加订单记录、减库存、减余额),那么如何保证数据的一致性?

(2)需求:如何保证各个数据库的数据一致性,避免脏读写(读写脏数据)。

(3)解决方案:seata。写隔离、读隔离。

(4)写隔离:解决多线程下写数据的冲突。避免脏写(写脏数据)。

  1. AT事务模式是两阶段提交协议的演变。​​​​​​​
    1. 一阶段本地事务提交前,需要确保先拿到全局锁;​​​​​​​
      1. 全局全局,即分布式全局锁就一把。
      2. 分布式全局锁是seata给我们提供的,在多线程情况下,只有一个线程能够拿到全局锁。​​​​​​​
      3. 分布式全局锁是从TC服务器那里拿的。
    2. 全局锁 的尝试被限制在一定范围内(源码中是尝试10次),超出范围(10次尝试)将放弃获取全局锁,并回滚本地事务,同时释放数据库的本地锁(行锁);
      1. 如果拿不到 全局锁 ,不能提交本地事务;
      2. 如果拿到全局锁,那就可以正常地修改数据库数据并持久化。​​​​​​​
  2. 官方文档以一个示例来说明: ​​​​​​​
    1. 两个或者多个全局事务 tx1 和 tx2,分别并发对 a 表的 m 字段进行更新操作,m 的初始值 1000;
    2. ​​​​​​​正常提交情况:
      1. 假设tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900,本地事务提交前,先拿到该记录的 全局锁 ,拿到了全局锁,本地提交并释放本地锁;
      2. tx2后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800本地事务提交前,尝试拿该记录的 全局锁 ,tx1全局提交前,该记录的全局锁一直会被 tx1 持有,tx2 需要重试等待 全局锁 ;如果在尝试范围内都获取不到全局锁,tx2下的所有分支事务都将回滚。
      3. tx1的二阶段全局提交完成,同时释放全局锁。此时,tx2才有机会拿到全局锁(如果还在尝试的话),也才有机会进行二阶段的全局提交(完成提交本地事务)。
      4. 如下图所示:
    3. 回滚的情况
      1. 如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚;
      2. 此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功;
      3. 因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题;
      4. 如下图所示:

        ​​​​​​​​​​​​​​
    4. 底层原理
      1. 本地锁,即数据库的行级锁。
      2. 底层其实就是通过jdbc来set数据库的自动提交等于0,然后在全局提交事务时commit手动提交。只是在手动commit事务之前,seata使用aop的方式增加了对锁的判断。

(5)读隔离:解决多线程下读数据的冲突。避免脏读(读脏数据)。

        在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,而Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)

        如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过在你的sql语句中加入 SELECT FOR UPDATE 语句的代理;这样才能改掉Seata读未提交的全局隔离级别,从而使全局变成读已提交或以上的隔离级别。

        SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试,这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回;

        出于总体性能上的考虑,Seata目前的方案并没有对所有SELECT语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句;

10 TC Server集群环境的使用案例

对于SpringBoot单体应用:

1、添加nacos客户端依赖;

<!-- nacos-client:排除掉旧版本,依赖新版本 -->
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>1.3.1</version>
</dependency>

2、配置application.properties文件

#----------------------------------------------------------
# Seata应用编号,默认为${spring.application.name}
seata.application-id=springcloud-order-seata
# Seata事务组编号,用于TC集群名
seata.tx-service-group=springcloud-order-seata-group
# 虚拟组和分组的映射
seata.service.vgroup-mapping.springcloud-order-seata-group=default

#seata-spring-boot-starter 1.1版本少一些配置项
seata.enabled=true
seata.registry.type=nacos
seata.registry.nacos.cluster=default
seata.registry.nacos.server-addr=192.168.172.128:8848
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.application=seata-server

#----------------------------------------------------------

对于Spring Cloud Alibaba微服务应用:

则不需要加nacos的jar包依赖,application.properties文件配置完全一样;

11 TCC模式的理解与应用

(1)TCC事务模式执行机制

        业务需求:AT模式(只用于关系型数据库)基本上能满足我们使用分布式事务大部分需求,但涉及非关系型数据库与中间件的操作、跨公司服务的调用、跨语言的应用调用就需要结合TCC模式;

     流原:TCC事务模式下,一个分布式的全局事务,整体也是两阶段提交(Try - [Comfirm/Cancel],共3个步骤)的模型,在Seata中,AT模式与TCC模式事实上都是基于两阶段提交,它们的区别在于:AT模式基于支持本地ACID事务的关系型数据库:

下面是AT事务模式的3个步骤:

  1. 第一步:prepare行为:在本地事务中,一并提交“业务数据更新“和”相应回滚日志记录”;(seata帮我们实现)
  2. 第二步:commit 行为:马上成功结束,自动异步批量清理回滚日志;(seata帮我们实现)
  3. 第三步:rollback 行为:通过回滚日志,自动生成补偿操作,完成数据回滚;(seata帮我们实现)

而TCC 模式,需要我们人为编写代码实现提交和回滚

  1. 第一步: prepare 行为:调用自定义的 prepare 逻辑;(真正要做的事情(逻辑操作),比如插入订单,更新库存,更新余额)
  2. 第一步:commit 行为:调用自定义的 commit 逻辑;(自己写代码实现,seata不帮我们实现)
  3. 第一步:rollback 行为:调用自定义的 rollback 逻辑;(自己写代码实现,seata不帮我们实现)

所以TCC模式,就是把自定义的分支事务的提交和回滚,并把分支事务纳入到全局事务管理中;

通俗来说,Seata的TCC模式就是手工版本的AT模式(手工写代码),它允许你自定义两阶段的处理逻辑而不需要依赖AT模式的undo_log回滚表(因为手动);

TCC事务模式应用实践

(2)案例1:基于SpringBoot单体应用的TCC事务

  1. 第一步:添加依赖。同AT。
    1. 第1步:nacos。目的是使用TC Server集群。
  2. 第二步:配置文件。同AT。
  3. 第三步:代码
    1. 第1步:程序入口类。同AT。
    2. 第2步:XxxController。同AT。
    3. 第3步:XxxService
      1. @LocalTCC注解。带参数方便在回滚方法中获取,并回滚。
      2. 案例:
        @LocalTCC
        public interface AccountService {
            /**
             * 扣除余额
             * 定义两阶段提交
             * name = reduceStock为一阶段try方法
             * commitMethod = commitTcc 为二阶段确认方法
             * rollbackMethod = cancel 为二阶段取消方法
             * BusinessActionContextParameter注解 可传递参数到二阶段方法
             *
             * @param userId 用户ID
             * @param money  扣减金额
             * @throws Exception 失败时抛出异常
             */
            @TwoPhaseBusinessAction(name = "reduceBalance", commitMethod = "commitTcc", rollbackMethod = "cancelTcc")
            void reduceBalance(@BusinessActionContextParameter(paramName = "userId") Integer userId,
                               @BusinessActionContextParameter(paramName = "money") BigDecimal money);

            /**
             * 确认方法、可以另命名,但要保证与commitMethod一致
             * context可以传递try方法的参数
             *
             * @param context 上下文
             * @return boolean
             */
            boolean commitTcc(BusinessActionContext context);

            /**
             * 二阶段取消方法
             *
             * @param context 上下文
             * @return boolean
             */
            boolean cancelTcc(BusinessActionContext context);
        }
    4. 第4步:XxxServiceImpl
      1. 业务方法。同AT。
        1. @OS注解:指定连接的数据源。同AT。
        2. @GlobalTransactional注解:全局事务。同AT。
      2. 手动编写提交逻辑代码:实现commitTcc()方法
        1. ​​​​​​​​​​​​​​true提交成功
        2. 如: 
      3. 手动编写回滚逻辑代码:实现cancelTcc()方法
        1. ​​​​​​​true回滚成功
        2. 如:​​​​​​​  
    5. 第5步:mapper。同AT。
    6. 第6步:实体类。同AT。
  4. 第四步:测试
    1. 如果哪步都成功了,seata会自动帮我们调用commitTcc()方法。
    2. 如果哪步有失败的,seata会自动帮我们调用cancelTcc()方法。

@LocalTCC注解标识此TCC为本地模式,即该事务是本地调用,非RPC调用,@LocalTCC一定需要注解在接口上,此接口可以是寻常的业务接口,只要实现了TCC的两阶段提交对应方法即可;

@TwoPhaseBusinessAction,该注解标识为TCC模式,注解try方法,其中name为当前tcc方法的bean名称,写方法名便可(全局唯一),commitMethod指提交方法,rollbackMethod指事务回滚方法,指定好三个方法之后,Seata会根据事务的成功或失败,通过动态代理去帮我们自动调用提交或者回滚;

@BusinessActionContextParameter 注解可以将参数传递到二阶段(commitMethod/rollbackMethod)的方法;

BusinessActionContext 是指TCC事务上下文,携带了业务方法的参数;

(3)案例2:基于Spring Cloud Alibaba的TCC分布式事务

​​​​​​​​​​​​​​​​​​​​​

  1. 第一步:依赖。同单体。 
  2. 第二步:配置文件。同单体。 
  3. 第三步:编码。同单体。 

具体代码实现和springboot单体应用的代码实现几乎没有区别,具体参考Git上提交的代码;

由于Seata出现时间并不长,也在不断的改进中,在实际面试中应该不会问大家比较底层的实现,同学们如果感兴趣的话,基于我们已有的源码阅读经验,可以看一下Seata的源码,它如何进行事务隔离保证数据一致性,官方提供的文档并不详细;

二 spring事务

1 思想、思路

        本章内容主要包含两部分:Spring所使用的操作数据库的技术之一,JDBC模板的使用;
另一部分则为Spring对于事务的管理。Spring与Dao部分,是Spring的两大核心技术IOC与AOP的典型应用体现:

  1. 对于JDBC模板的使用,是IOC的应用,是将JDBC模板对象注入给了Dao层的实现类。
  2. 对于Spring的事务管理,是AOP的应用,将事务作为切面织入到了Service层的业务方法中。

        事务原本是数据库中的概念,在Dao层。但一般情况下,需要将事务提升到业务层,即Service层。这样做是为了能够使用事务的特性来管理具体的业务。

2 体系组织(3种事务管理、2个事务相关的接口)

(1)Spring中三种方式来实现对事务的管理:

  1. 使用Spring的事务代理工厂管理事务。
  2. 使用Spring的事务注解管理事务。
  3. 使用AspectJ的AOP配置管理事务。 

(2)Spring的2个事务相关的接口。

  1. 事务管理器接口
    1. 事务管理器是PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息:
    2. 常用的两个实现类:
      PlatformTransactionManager接口有两个常用的实现类。
      DataSoureTransactionManager:使用JDBC或iBatis进行持久化数据时使用。
      HibernateTransactionManager:使用Hibernate进行持久化数据时使用。
    3. Spring 的回滚方式:
      Spring事务的默认回滚方式是:发生运行时异常时回滚,发生受查异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。
  2. 事务定义接口。事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:
    1. 事务隔离级别
    2. 事务传播行为
    3. 事务默认超时时限
    4. 及对它们的操作

3 流原

  1. 程序举例环境搭建
    1. Step1:创建数据库表
    2. Step2:创建实体类
    3. Step3:定义dao接口
    4. Step4:定义dao实现类
    5. Step5:定义异常类
    6. Step6:定义Service接口
    7. Step7:定义service的实现类
    8. Step8:Spring 配置文件中添加最全约束
    9. Step9:修改Spring配置文件内容
      1. 配置cp30数据源
      2. IOC、Dao、Service
    10. Step10:定义测试类
  2. 使用Spring的事务代理工厂管理事务
    1. Step1:复制项目
    2. Step2:导入jar包
    3. Step3:在容器中添加事务管理器DataSourceTransactionManager:
    4. Step4:在容器中添加事务代理TransactionProxyFactoryBean
    5. Step5:修改测试类
  3. 使用Spring的事务注解管理事务
    1. Step1:复制项目
    2. Step2:在容器中添加事务管理器
    3. Step3:在Service实现类方法上添加注解
    4. Step4:修改配置文件内容
    5. Step5:修改测试类
  4. 使用AspectJ的AOP配置管理事务(重点)
    1. Step1:复制项目
    2. Step2:导入jar包
    3. Step3:在容器中添加事务管理器
    4. Step4:配置事务通知
    5. Step5:配置顾问。指定将配置好的事务通知,织入给谁。
    6. Step6:修改测试类

三 mybatis事务

1 Mybatis与jdbc的关系

        Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC Connection 对象的 commit(), rollback()完成提交与回滚 。使用Connection 对象的 setAutoCommit() 方法来设置事务提交方式的(自动提交和手工提交)。
        Mybatis 框架的事务控制方式有两种,一种是容器进行事务管理的,一种是程序员手工决定事务的提交与回滚。

2 事务控制基本操作流程

(1)mybatis.xml中配置事务管制器类型

        <!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚) -->
        < transactionManager type ="JDBC" />

(2)java代码

        //1.mybatis 主配置文件
                String config = "mybatis-config.xml" ;
        //2.读取配置文件
                InputStream in = Resources.getResourceAsStream(config);
        //3.创建 SqlSessionFactory 对象
                SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //4.获取 SqlSession
                SqlSession session = factory.openSession();
        //5.创建保存数据的对象
                Student student = new Student();
                student.setId( 1005 );
                student.setName( " 张丽 " );
                student.setEmail( "zhangli@163.com" );
                student.setAge( 20 );
        //6.执行插入 insert
                int rows = session.insert(
                "com.bjpowernode.dao.StudentDao.insertStudent" ,student);
        //7.提交事务
                session.commit();
                System. out .println( " 增加记录的行数 :" +rows);
        //8.关闭 SqlSession
                session.close();

3 默认需要手动提交事务(事务管理器)

  1. <transactionManager type="JDBC"/> 该标签用于指定 MyBatis所使用的事务管理器。
  2. MyBatis 支持两种事务管理器类型:JDBC MANAGED
    1. JDBC:使用 JDBC 的事务管理机制。即,通过 Connection 的 commit()方法提交,通过 rollback()方法回滚。但默认情况下,MyBatis 将自动提交功能关闭了,改为了手动提交。即程序中需要显式的对事务进行提交或回滚。从日志的输出信息中可以看到。
    2. MANAGED :由容器来管理事务的整个生命周期(如 Spring 容器)。
  3. 注:推荐手动提交事务。因为复杂业务中一个事务会包括多个DML操作,自动提交只能做到一个事务只有一个DML操作。

例如:```java public class StudentServiceImpl implements StudentService {

public boolean addStudent(Student student) {
    boolean b = false;
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try{
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        int i = studentDAO.insertStudent(student);
        b = i>0;
        sqlSession.commit();
    }catch (Exception e){
        sqlSession.rollback();
    }
    return b;
}

} ```

 4 自动提交事务

        设置自动提交的方式,factory openSession() 分为有参数和无参数的。
        有参数为 true ,使用自动提交,可以修改 MyBatisUtil getSqlSession() 方法。
        session = factory .openSession( true );
        再执行 insert 操作,无需执行 session.commit(), 事务是自动提交的
注:推荐手动提交事务。因为复杂业务中一个事务会包括多个DML操作,自动提交只能做到一个事务只有一个DML操作。

例如:MyBatisUtil优化

```java public class MyBatisUtil {

private static SqlSessionFactory factory;
private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();

static{
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(is);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static SqlSessionFactory getFactory(){
    return factory;
}

private static SqlSession getSqlSession(boolean isAutoCommit){
    SqlSession sqlSession = local.get();
    if(sqlSession == null ){
        sqlSession = factory.openSession(isAutoCommit);
        local.set(sqlSession);
    }
    return sqlSession;
}

//手动事务管理
public static SqlSession getSqlSession(){
    return getSqlSession(false);
}

//自动事务提交
public static <T extends Object>T getMapper(Class<T> c){
    SqlSession sqlSession = getSqlSession(true);
    return sqlSession.getMapper(c);
}

} ```

业务逻辑层自动事务管理

```java public class StudentServiceImpl implements StudentService {

private StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);

public boolean addStudent(Student student) {
    int i = studentDAO.insertStudent(student);
    boolean b = i>0;
    return b;
}

} ```

四 hibernate事务

1 事务控制基本操作流程

        在Hibernate 中,可以通过代码来操作管理事务,如:

  1. 通过“Transaction tx =session. beginTransaction(),"开启一个事务;
  2. 持久化操作后,
  3. 通过“tx. commit();”提交事务;
  4. 如果事务出现异常,又通过“tx rollback);"操作来撤销事务(事务回滚)。

2 事务的并发问题

        事务的并发问题,主要发生在读与写两类操作上。

(1)读并发

        读上有三类并发问题:脏读、不可重复读、幻读。

(2)写并发

        写上有两类并发问题:第一类丢失更新(回滚丢失更新),第二类丢失更新(提交丢失
更新)。

3 加锁机制  

        通过加锁可以解决写并发问题。锁可以分为两类:
        乐观锁(Optimistic lock) :每次访问数据时,都会乐观的认为其它事务此时肯定不会同时
修改该数据。但在真正修改时,会在代码中先判断数据是否已经被其它事务修改过。所以锁
是加在代码中的。
        悲观锁(Pessimistic lock) :每次访问数据时,都会悲观的认为其它事务一定会同时修改该
数据。所以,其在访问数据时,在数据库中就会先给数据加锁,以防止其它事务同时修改该
数据。所以锁是加在数据库中的。

4 Hibernate 并发问题解决

(1)设置 Hibernate 事务隔离级别

        Hibernate 建议设置事务隔离级别为 4 级,即可重复读。从 Hibernate 框架解压目录\project\etc\hibernate.properties 的默认设置中可以看到。

Hibernate 配置文件中可以对隔离级别进行设置。 

(2)Hibernate 实现乐观 

        在 Hibernate 映射文件的<class/>标签中,有一个子标签<version/>,其 name 属性用于指 定作为版本的属性名称。其还有一个子标签<timestamp/>用于指定作为时间戳的属性名称。 这两个子标签的用法相同,不同的是,作为版本的属性要求其类型为 int,而作为时间 戳的属性,要求为其类型为 java.sql.timestamp

        举例:optimisticlock
        实体 Bean 中要增加一个用于记录版本的属性,要求类型为 int 。其值由系统自动维护:
初始值为 0 ,每修改一次自动增 1

映射文件中增加<version/> 标签,要求在 <id/> <property/> 之间。

(3)Hibernate 实现悲观

1)添加排他锁       

         对于排他锁,其加锁的时间为通过 get()方法执行 select 查询后,解锁时间为当前事务结 束。这期间,当前事务是可以修改被其加锁的数据的,但其它事务是无法对该数据进行修改的。

在测试时请注意,在 student.setName() 处添加断点,当运行到此断点后,到数据库中修
id 1 student 的任意属性,做保存后,关闭表。此时 DB 会给出“未响应”提示。说
明,数据被锁定了,不能修改。

查看控制台输出的 SQL 语句,可看到 select 语句后添加了 for update。证明添加了写锁。  

 

2)添加共享锁

   对于共享锁,其加锁的时间也为通过 get()方法执行 select 查询后,但其解锁时间则是在其查询的数据被检索出来后的瞬间。即从程序运行来看,get()方法开始执行则加上了读锁,get()方法运行结束,则读锁解除。所以,加了读锁的 get()方法后的修改语句,与读锁是没有任何关系的。

从后台运行的 SQL 中可以看到,多了 lock in share mode,说明添加了共享锁。 

5 service层的事务管理(JTA)

        到这我们已经设置了事务的隔离级别,那么我们在真正进行事务管理的时候,需要考虑事务的应用的场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个DAQ实现一个业务逻辑的操作。具体操作如下显示:

五 jdbc事务

1 事务控制基本工作流程

  1. 自动提交:在JDBC中,事务操作缺省是自动提交。
    • 一条对数据库的DML(insert、update、delete)代表一项事务操作
    • 操作成功后,系统将自动调用commit()提交,否则自动调用rollback()回滚
    • 事务开始的边界则不是那么明显了,它会开始于组成当前事务的所有statement中的第一个被执行的时候。
    • 事务结束的边界是commit或者rollback方法的调用
  2. 手动提交:在JDBC中,获得Connection对象开始事务、提交或回滚事务、关闭连接。其事务策略是
    1. 第1步:获得Connection对象(建立一个数据库连接)
    2. 第2步:打开事务,同时关闭事务的自动提交:conn.setAutoComit(false);//true等价于1, false等价于0
    3. 第3步:手动提交事务:conn.commit();
    4. 第4步:手动回滚事务  conn.rollback();
      1. 比如:出现异常,在异常捕获时调用rollback()进行回滚,回复至数据初始状态 
  3. 部分提交:
    1. 当只想撤销事务中的部分操作时可使用SavePoint SavePoint sp = connection.setSavepoint(); connection.rollerbak(sp);connection.commit();    

2 service层事务控制:基本方法

  1. 第一步:获得Connection对象(建立一个数据库连接)
  2. 第二步:关闭事务的自动提交:conn.setAutoComit(false);//true等价于1, false等价于0
  3. 第三步:手动提交事务:conn.commit();
  4. 第四步:手动回滚事务  conn.rollback();
    1. 出现异常 

3 service层事务控制:传递connection法,跨多线程(污染接口)

        为了解决线程中Connection对象不同步的问题,可以将Connection对象通过service传递给各个DAO方法吗?

  1. 如果使用传递Connection,容易造成接口污染(BadSmell)。
  2. 定义接口是为了更容易更换实现,而将Connection定义在接口中,会造成污染当前接口。

4 service层事务控制:ThreadLocal法,跨多线程

        可以将整个线程中(单线程)中,存储一个共享值。线程拥有一个类似Map的属性,键值对结构<ThreadLocal对象,值>。一个线程共享同一个ThreadLocal,在整个流程中任一环节可以存值或取值。

5 service层事务控制:跨越多个数据源的事务,使用JTA容器实现事务

  1. 跨越多个数据源的事务,使用JTA容器实现事务。 分成两阶段提交。
    1. javax.transaction.UserTransaction tx = (UserTransaction)ctx.lookup(“jndiName");     tx.begin();   
    2. //connection1 connection2 (可能来自不同的数据库)…    
    3. tx.commit();//tx.rollback();

6 隔离级别多线程并发读取数据时的正确性

六 jdbc的本质

七 mysql事务 

1 事务的业务与需求

        正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句搞定,那么事务则没有存在的价值了。

2 思想思路

  1. 一个事务其实就是一个完整的业务逻辑。

3 流原:事务是怎么做到多条DML语句同时成功和同时失败的呢?

  1. 事务开启:Start Transaction
    1. InnoDB存储引擎:提供一组用来记录事务性活动的日志文件。
    2. 在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。
  2. 提交事务:commit
    1. 清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。
    2. 提交事务标志着,事务的结束。并且是一种全部成功的结束。
  3. 回滚事务?(roolbak:回滚永远都是只能回滚到上一次的提交点!)
    1. 清空事务性活动的日志文件,并且将之前所有的DML操作全部撤销
    2. 回滚事务标志着,事务的结束。并且是一种全部失败的结束。

4 mysql事务自动提交

  1. mysql默认情况下是支持自动提交事务的(自动提交)。
    1. 什么是自动提交?每执行一条DML语句,则提交一次!
    2. 这种自动提交实际上是不符合我们的开发习惯,因为一个业务通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全,必须要求同时成功之后再提交,所以不能执行一条就提交一条。
    3. 自动提交与非自动提交的特点
      1. 启用自动提交模式:
        1. 如果自动提交模式被启用,则单条 DML 语句将缺省地开始一个新的事务。
        2. 如果该语句执行成功,事务将自动提交,并永久地保存该语句的执行结果。
        3. 如果语句执行失败,事务将自动回滚,并取消该语句的结果。
        4. 在自动提交模式下,仍可使用 START TRANSACTION 语句来显式地启动事务。这时,一个事务仍可包含 多条语句,直到这些语句被统一提交或回滚。
      2. 禁用自动提交模式:
        1. 如果禁用自动提交,事务可以跨越多条语句。
        2. 在这种情况下,事务可以用 COMMIT ROLLBACK 语句来显式地提交或回滚。
  2. 查看mysql自动提交事务模式
    1. mysql> SHOW VARIABLES LIKE 'autocommit';
      +---------------+-------+
      | Variable_name | Value |
      +---------------+-------+
      | autocommit    | ON    |
      +---------------+-------+
      1 row in set, 1 warning (0.04 sec)
      结果显示,autocommit 的值是 ON,表示系统开启自动提交模式。
  3. 开启或关闭mysql自动提交事务功能
    1. 在 MySQL 中,可以使用 SET autocommit 语句设置事务的自动提交模式,语法格式如下:

            SET autocommit = 0|1|ON|OFF;

      对取值的说明:
    2. 值为 0 和值为 OFF:关闭事务自动提交。如果关闭自动提交,用户将会一直处于某个事务中,只有提交或回滚后才会结束当前事务,重新开始一个新事务。
    3. 值为 1 和值为 ON:开启事务自动提交。如果开启自动提交,则每执行一条 SQL 语句,事务都会提交一次。

        MySQL设置事务自动提交(开启和关闭)

5 事务的4大特性

事务包括4个特性:ACID

  1. 原子性:不可再分。
  2. 一致性:在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证前后数据的一致性。
  3. 隔离性:
           教室A和教室B之间有一道墙,这道墙就是隔离性。
           两个事务同时去操作一张表:A事务在操作一张表的时候,另一个事务B也操作这张表会那样???这就是隔离性。
           相当于多线程并发访问同一张表一样,此时就会有多线程带来的线程安全问题。为了解决事务之间的线程安全问题,就要使用事务的隔离性。      
  4. 持久性:将数据持久化到硬盘上的数据库中。

6  重点研究一下事务的隔离性!!!

  1. 事务的隔离级别:A教室和B教室中间有一道墙,这道墙可以很厚,也可以很薄。这道墙越厚,表示隔离级别就越高。

  2. 分类:

    1. 读未提交(最低级别):没有提交就读到了

      1. 原因:事务A可以读取到事务B未提交的数据。

      2. 问题:脏数据,脏读现象

    2. 读已提交:提交之后才能读到。数据绝对真实,oracle默认。

      1. 原因:事务A只能读取到事务B提交之后的数据。

      2. 解决:读未提交

      3. 问题:不可重复读

               什么是不可重复读取数据呢?在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,3不等于4称为不可重复读取。

    3. 可重复读:即使提交之后也读不到,永远读取的都是事务A刚开启事务时的数据。数据不够真实,出现幻影读。mysql默认。

      1. 原因:事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。

      2. 解决:不可重复读

      3. 问题:可以会出现幻影读。每一次读取到的数据都是幻象。不够真实!

      4. 案例:以下需求应该使用什么样的隔离级别?可重复读即可。
        下午1点开始开启了事务,只要事务不结束,到下午3点,读到的数据还是那样!
        原理:就是做了数据的一个备份,或者说对数据进行了一个快照。

    4. 序列化/串行化(最高级别):

      1. 原因:事务排队,需要等待其他事务的提交,不能并发!有类似于java中的synchronized,线程同步(事务同步)。

      2. 解决:解决了以上所有问题(读未提交、读已提交、可重复读)

      3. 问题:每一次读取到的数据都是最真实的,并且效率是最低的。

7 事务的提交与回滚演示

  1. 第一步:创建表
  2. 第二步:查询表中数据
  3. 第三步:开启事务
  4. 第四步:插入数据
  5. 第五步:查看数据
  6. 第六步:修改数据
  7. 第七步:查看数据
  8. 第八步:回滚事务
  9. 第九步:查看数据

8 事务隔离级别设置

高性能MySQL学习总结及实验验证:

数据库隔离级别有四种,应用《高性能mysql》一书中的说明:

然后说说修改事务隔离级别的方法:

  1. 全局修改,修改mysql.ini配置文件,在最后加上:
    1. #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
    2.  [mysqld]
    3.  transaction-isolation = REPEATABLE-READ
    4. 说明:这里全局默认是REPEATABLE-READ,其实MySQL本来默认也是这个级别
  2. 对当前session修改,在登录mysql客户端后,执行命令:
    1. set session transaction isolation level read uncommitted;

参考:MySQL设置事物隔离级别

八 oracle事务

1oracle数据库的事务由下列语句组成:

  1.  一组DML语句,修改的数据在他们中保持一致
  2. 一个DDL (Data Define Language) 语句
  3. 一个DCL (Data Control Language)语句    

2 事务的开始与结束 

  1. 开始于:第一个执行的DML语句
  2. 结束于:
    1. commit 或 rollback
    2. DDL or DCL语句(隐式的提交事务)
    3. 用户连接异常,或用户断开连接(隐式的回滚)
    4. 系统崩溃(隐式的回滚)

3 事务的自动提交设置

        因为 oracle 的这种机制,所以有的工具增加了进行自动提交的设置,就是对于需要显示提交的,工具检测出来后,自动的给加上 commit。看着的效果就是不需要执行 commit 就能生效,其实是后台在后面自动给你执行了 commit
Oracle SQL Developer启用(关闭)自动提交事务,设置自动commit

        若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后,系统将自动进行提交,这就是自动提交。其格式为:SQL>SET AUTOCOMMIT ON;

 oracle如何实现自动提交,Oracle sqlplus参数autocommit(自动提交)的设置

oracle中如何设置自动提交

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值