zoukankan      html  css  js  c++  java
  • 事务

    原子性:事务是不可分割的。 

    一致性:保证数据在事务的执行周期内,是一致的! 

    隔离型:多个事务之间的干扰关系!隔离级别! 

    持久性:事务一旦被提交,就不可能再被回滚!

    隔离级别

    事务并发带来的问题前文已经描述得非常仔细了。事务的隔离级别就是为了针对并发出现的问题,不同的级别可以保证不同的一致性。

    为了解决上面讲到的并发事务处理带来的问题,SQL标准提出了4个等级的事务隔离级别。不同的隔离级别在同样的环境下会出现不同的结果。 

     

    下面看看四种隔离级别的比较

    隔离级别

    读数据一致性

    脏读

    不可重复读

    幻读

    未提交读(Read uncommitted)

    最低级别,只能保证不读取物理上损坏的数据

    已提交读(Read committed)

    语句级

    可重复读(Repeatable read)

    事务级

    可序列化(Serializable)

    最高级别,事务级

     

    Spring事务的隔离级别(isolation)

     1. DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

          另外四个与JDBC的隔离级别相对应

     2. READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。

          这种隔离级别会产生脏读,不可重复读和幻像读。

     3. READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据

     4.REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。

          它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

     5. SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。

          除了防止脏读,不可重复读外,还避免了幻像读。

     

    Spring事务的传播机制(propagation)

    它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播

     

    1: REQUIRED

    当前正要执行的事务不在另外一个事务里,那么就起一个新的事务,总之要求在事务里面。

    比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,

    ServiceA.methodA已经起了事务,这时调用 .methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA

    的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。

    这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被

    提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。[关键点:是同一个事务]

    class1.fun1--->class2.fun2: fun1调用fun2 ,无论在fun1还是fun2里发生unchecked异常[不论是否catch处理异常],都会触发整个方法的回滚.

     

    2:REQUIRES_NEW

    这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,

    那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,

    他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在

    两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。[关键点:2个事务是单独的,没有依赖关系]如果ServiceB.methodB失败回滚,

    如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

     

    class1.fun1--->class2.fun2: fun1调用fun2 ,

    如果fun2抛出异常且被catch处理,则fun2回滚,fun1不回滚.

    如果fun2抛出异常且没被catch处理,则fun2,fun1都回滚.

    如果fun1抛出异常,则fun1回滚,fun2不回滚. 

    3: SUPPORTS

    如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行

     

    4:NOT_SUPPORTED

    当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,

    那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

     

    5:MANDATORY

    必须在一个事务中运行。也就是说,他只能被一个父事务调用。直接调用,他就要抛出异常

     

    6: NEVER

    不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,

    那么ServiceB.methodB就要抛出异常了。

     

    7:NESTED

    理解Nested的关键是savepoint。他与REQUIRES_NEW的区别是,REQUIRES_NEW另起一个事务,将会与他的父事务相互独立

    而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。

     [关键点:2个事务是依赖关系,fun2依赖fun1]

     class1.fun1--->class2.fun2: fun1调用fun2,

    如果fun2抛出异常且在fun1里catch处理了,则fun2回滚,fun1不回滚, 如果没有catch,则fun1也回滚.

    如果fun1抛出异常,则fun1和fun2都回滚.

     

    我们知道,应用中的一个业务逻辑,往往由多条语句组合完成。那么我们就可以简单地将事务理解为一组SQL语句的集合,要么这个集合全部成功集合,要么这个集合就全部失败退回到第一句之前的状态。

    所以我决定先来介绍事务的语法 

    1. 开启事务start transaction,可以简写为 begin 

    2. 然后记录之后需要执行的一组sql 

    3. 提交commit 

    4. 如果所有的sql都执行成功,则提交,将sql的执行结果持久化到数据表内。 

    5. 回滚rollback 

    6. 如果存在失败的sql,则需要回滚,将sql的执行结果,退回到事务开始之时 

    7. 无论回滚还是提交,都会关闭事务!需要再次开启,才能使用。 

    8. 还有一点需要注意,就是事务只针对当前连接

     

    基本原理

    语法说完了,浮躁的人也不用继续看下去了。下面简单说一下事务的基本原理吧。 

    提交,就会将结果持久化,不提交就不会。 

    如果我们不开启事务,只执行一条sql,马上就会持久化数据,可以看出,普通的执行就是立即提交。 

    这是因为MySQL默认对sql语句的执行是自动提交的

    也就是说,开启事务,实际上就是关闭了自动提交的功能,改成了commit手动提交!

    我们可以通过简单的对是否自动提交加以设置,完成开启事务的目的! 

    自动提交的特征是保存在服务的一个autocommit的变量内。可以进行修改:

    说明: 这里写图片描述

    还需要注意一点,就是事务类似于外键约束,只被innodb引擎支持。

    特点

    下面来说说事务的特点ACID。也就是原子性一致性隔离性持久性

    原子性:事务是不可分割的。 

    一致性:保证数据在事务的执行周期内,是一致的! 

    隔离型:多个事务之间的干扰关系!隔离级别! 

    持久性:事务一旦被提交,就不可能再被回滚!

    事务并发

    事务并发会带来一些问题,所以才有了不同的事务隔离级别。要想了解事务的隔离级别,就必须首先了解事务并发会带来的问题。 

    一般来说,会出现三类数据读问题和数据更新问题。

    脏读

    一个事务正在对一条记录做修改,但未提交,另一个事务读取了这些脏数据,并进一步处理,就会产生未提交的数据依赖。 

    举一个例子:

    时间

    转账事务A

    取款事务B

    T1

     

    开始事务

    T2

    开始事务

     

    T3

     

    查询账户余额为1000元

    T4

     

    取出500元把余额改为500元

    T5

    查询账户余额为500元(脏读)

     

    T6

     

    撤销事务余额恢复为1000元

    T7

    汇入100元把余额改为600元

     

    T8

    提交事务

     

    A读取了B尚未提交的脏数,导致最后余额为600元。

    不可重复读

    一个事务在不同时间读取数据不一致。 

    举一个例子:

    时间

    取款事务A

    转账事务B

    T1

     

    开始事务

    T2

    开始事务

     

    T3

     

    查询账户余额为1000元

    T4

    查询账户余额为1000元

     

    T5

     

    取出100元把余额改为900元

    T6

     

    提交事务

    T7

    查询账户余额为900元(和T4读取的不一致)

     

    可以看到最后读取的数据不一致。

    幻读

    幻读和不可重复读的概念类似,都是不同时间数据不一致,只不过幻读是针对新增数据,而不可重复读是针对更改数据 

    看一个例子:

    时间

    统计金额事务A

    转账事务B

    T1

     

    开始事务

    T2

    开始事务

     

    T3

    统计总存款数为10000元

     

    T4

     

    新增一个存款账户,存款为100元

    T5

     

    提交事务

    T6

    再次统计总存款数为10100元(幻象读)

     

    更新丢失

    两个事务对同一数据进行更新,后者会覆盖先者的更新。

    时间

    取款事务A

    转账事务B

    T1

    开始事务

     

    T2

     

    开始事务

    T3

    查询账户余额为1000元

     

    T4

     

    查询账户余额为1000元

    T5

     

    汇入100元把余额改为1100元

    T6

     

    提交事务

    T7

    取出100元将余额改为900元

     

    T8

    撤销事务

     

    T9

    余额恢复为1000元(丢失更新)

     
  • 相关阅读:
    Redis主从复制、哨兵Sentinel、集群简单介绍
    Redis集群
    Redis哨兵模式
    Redis主从架构
    Redis持久化方式
    缓存2.2——Redis并发竞争
    DOM内容梳理2
    纯js制作九宫格
    正则表达式内容梳理
    JavaScript之DOM详解
  • 原文地址:https://www.cnblogs.com/domi22/p/8059341.html
Copyright © 2011-2022 走看看