【事务】四大特性。隔离级别。脏读 不可重复读 幻读。2类丢失更新。事务的传播行为。

https://blog.csdn.net/taozi8023/article/details/51484940

事务四大特征:

  • 原子性,

    • atomicity
  • 一致性,

    • consistency
  • 隔离性

    • isolation
  • 和持久性(ACID)

    • 持久性(Durability)

原子性

事务必须是一个不可分割的整体,就像我们在化学课里学到的原子,原子是构成物质的最小单位。于是,人们就归纳出事务的第一个特性:原子性(Atomicity)

一致性

一致性(Consistency)。也就是说,执行完数据库操作后,数据不会被破坏。打个比方,如果从 A 账户转账到 B 账户,不可能因为 A 账户扣了钱,而 B 账户没有加钱吧。如果出现了这类事情,您一定会非常气愤

隔离性

有可能别人也提交了一条 delete 语句到数据库中。也许我们都是对同一条记录进行操作。我们必须保证数据库操作之间是“隔离”的(线程之间有时也要做到隔离),彼此之间没有任何干扰。这就是:隔离性(Isolation)。

持久性

当我们执行一条 insert 语句后,数据库必须要保证有一条数据永久地存放在磁盘中,这个也算事务的一条特性, 它就是:持久性(Durability)。

事务隔离级别

让各个数据库厂商都支持我们的规范!”,这个规范就是:事务隔离级别(Transaction Isolation Level)。

  1. READ_UNCOMMITTED 读 未提交
  2. READ_COMMITTED 读 提交
  3. REPEATABLE_READ 可重复的 读
  4. SERIALIZABLE 序列化的

从上往下,级别越来越高,

  • 并发性越来越差,
  • 安全性越来越高,反之则反。

其实想想也就清楚了:

  • 原子性是基础,
  • 隔离性是手段,
  • 持久性是目的,
  • 真正的老大就是一致性。

三类数据读问题

  1. Dirty Read(脏读)
  2. Unrepeatable Read(不可重复读)
  3. Phantom Read(幻读)

两类数据更新问题

  1. 第一类丢失更新
  2. 第二类丢失更新

三类数据读问题

脏读

首先看看“脏读”,比如说,有两个事务,它们在并发执行(也就是竞争)。

img

余额应该为 1100 元才对!请看 T6 时间点,事务 B 此时查询余额为 900 元,这个数据就是脏数据,它是事务 A 造成的,明显事务没有进行隔离,渗过来了,乱套了。

余额应该为 1100 元才对!请看 T6 时间点,事务 B 此时查询余额为 900 元,这个数据就是脏数据,它是事务 A 造成的,明显事务没有进行隔离,渗过来了,乱套了。

不可重复读

img

事务 A 其实除了查询了两次以外,其他什么事情都没有做,结果钱就从 1000 变成 0 了,这就是重复读了。

其实这样也是合理的,毕竟事务 B 提交了事务,数据库将结果进行了持久化,所以事务 A 再次读取自然就发生了变化。

但在有些变态的场景下却是不允许的。毕竟这种现象也是事务之间没有隔离所造成的,但我们对于这种问题,似乎可以忽略。

幻读

其实意义就是鬼在读,不是人在读,或者说搞不清楚为什么,它就变了,很晕,真的很晕。

img

这同样也是事务没有隔离所造成的,但对于大多数应用系统而言,这似乎也是正常的,可以理解,也是允许的。

银行里那些恶心的那些系统,要求非常严密,统计的时候,甚至会将所有的其他操作给隔离开,

这种隔离级别就算非常高了(估计要到 SERIALIZABLE 级别了)。

两类数据更新问题

第一类丢失更新

A事务撤销时,把已经提交的B事务的更新数据覆盖了。

这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来:

img

但是,在当前的四种任意隔离级别中,都不会发生该情况,不然绝对乱套,

  • 我都没提交事务只是撤销,就把别人的给覆盖了,这也太恐怖了。

第二类丢失更新

B事务覆盖A事务已经提交的数据,造成A事务所做操作丢失

img

脏读:

  • 事务 A 读取了事务 B 未提交的数据,并在这个基础上又做了其他操作。

不可重复读:

  • 事务 A 读取了 事务 B 已提交的更改数据。

幻读:

  • 事务 A 读取了事务 B 已提交的新增数据。

第一条是坚决抵制的,后两条在大多数情况下可不作考虑。

img

JDBC 也提供了这四类事务隔离级别,

MySQL 数据库的默认事务隔离级别就是 READ_COMMITTED,

Oracle、SQL Server、DB2等都有有自己的默认值。

Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。

  • 所以Oracle不支持脏读,即Oracle中不允许一个会话读取其他事务未提交的数据修改结果,从而防止了由于事务回滚发生的读取不正确。

  • Oracle缺省的配置是Read Committed隔离级别(也称为语句级别的隔离),

当插入数据时,就锁定表,这叫“锁表”;当更新数据时,就锁定行,这叫“锁行”。当

事务的传播行为

除了 JDBC 给我们提供的事务隔离级别这种解决方案以外,

不妨看看 Spring 的解决方案吧,其实它是对 JDBC 的一个补充或扩展。

就是:事务传播行为(Transaction Propagation Behavior)。

确实够牛逼的,Spring 一下子就提供了 7 种事务传播行为

PROPAGATION_REQUIRED 必需的
RROPAGATION_REQUIRES_NEW 必需的 新的
PROPAGATION_NESTED 嵌入
PROPAGATION_SUPPORTS 支持的
PROPAGATION_NOT_SUPPORTED 不支持的
PROPAGATION_NEVER 从不
PROPAGATION_MANDATORY 强制性的

propagation 
英 /ˌprɒpəˈɡeɪʃn/  美 /ˌprɑːpəˈɡeɪʃn/  全球(美国)  
简明 韦氏  例句  百科
n. (动植物等的)繁殖,增殖,;(观点、理论等的)传播;(运动、光线、声音等的)传送


required 
英 /rɪˈkwaɪəd/  美 /rɪˈkwaɪərd/  全球(英国)  
简明 柯林斯 例句  百科
adj. 必需的;(美)必修的
v. 需要(require 的过去式及过去分词形式);要求

nested 
英 /'nestɪd/  美 /ˈnestɪd/  全球(英国)  
简明 柯林斯 例句  百科
adj. 嵌套的,内装的
v. 筑巢;嵌入(nest 的过去分词)


mandatory 
英 /ˈmændətəri; mænˈdeɪtəri/  美 /ˈmændətɔːri/  全球(美国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
adj. 强制性的,义务的;受(前国际联盟)委任统治的
n. 受托人,代理人(=mandatary)
复数 mandatories

事务是从哪里来?传播到哪里去?答案是,从方法 A 传播到方法 B。

Spring 解决的只是方法之间的事务传播,那情况就多了,比如:

方法 A 有事务,方法 B 也有事务。 有 有
方法 A 有事务,方法 B 没有事务。有 没有
方法 A 没有事务,方法 B 有事务。 没有 有
方法 A 没有事务,方法 B 也没有事务。 没有 没有

这样就是 4 种了,还有 3 种特殊情况。


@Transactional  
void A(){  
}  

@Transactional  
void B(){  
   A();  
} 

假设事务从方法 A 传播到方法 B,您需要面对方法 B,问自己一个问题:

方法 A 有事务吗?

  1. 如果没有,就开启一个事务如果有,就加入当前事务(方法A加入到方法B)。这就是 PROPAGATION_REQUIRED,它也是 Spring 提供的默认事务传播行为,适合绝大多数情况。
  2. 如果没有,就开启一个事务;如果有,就将当前事务挂起。这就是 RROPAGATION_REQUIRES_NEW,意思就是创建了一个新事务,它和原来的事务没有任何关系了。
  3. 如果没有,就开启一个事务;如果有,就在当前事务中嵌套其他事务。这就是 PROPAGATION_NESTED,也就是传说中的**“嵌套事务”**了,所嵌套的子事务与主事务之间是有关联的(当主事务提交或回滚,子事务也会提交或回滚)。
  4. 如果没有,就以非事务方式执行;如果有,就使用当前事务。这就是 PROPAGATION_SUPPORTS,这种方式非常随意,没有就没有,有就有,有点无所谓的态度,反正我是支持你的。
  5. **如果没有,就以非事务方式执行;如果有,就将当前事务挂起。**这就是 PROPAGATION_NOT_SUPPORTED,这种方式非常强硬,没有就没有,有我也不支持你,把你挂起来,不鸟你。
  6. **如果没有,就以非事务方式执行;如果有,就抛出异常。**这就是 PROPAGATION_NEVER,这种方式更猛,没有就没有,有了反而报错,确实够牛的,它说:我从不支持事务!
  7. **如果没有,就抛出异常;如果有,就使用当前事务。**这就是 PROPAGATION_MANDATORY,这种方式可以说是牛逼中的牛逼了,没有事务直接就报错,确实够狠的,它说:我必须要有事务!

propagation 事务7种传播行为:

如果没有,就开启一个事务

  • required 必需的 默认, 如果有,就加入当前事务

  • REQUIRES_NEW 必需的 新的。如果有,就将当前事务挂起,创建一个新的

  • nested 嵌入 。嵌套。主事务 提交或回滚,子事务也会提交或回滚

如果没有,就以非事务方式执行

  • supports 支持的 。没有就没有,有就有,我支持你。

  • NOT_SUPPORTED 不支持的 。如果有,就将当前事务挂起。不鸟你。

  • NEVER 从不。如果有,就抛出异常。

如果没有,就抛出异常;如果有,就使用当前事务。

  • mandatory 强制性的

Spring 给我们带来了事务传播行为 和 附加的功能:

事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,所以故意给事务设置一个最大时常,如果超过了,就回滚事务。
只读事务(Readonly Transaction):为了忽略那些不需要事务的方法,比如读取数据,这样可以有效地提高一些性能。
推荐大家使用 Spring 的注解式事务配置,而放弃 XML 式事务配置。


<tx:annotation-driven/>  

在需要事务的方法上使用:

@Transactional  
public void xxx() {  
    ...  
} 

可在 @Transactional 注解中设置:

  • 事务隔离级别、
  • 事务传播行为、
  • 事务超时时间、
  • 是否只读事务。

img

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值