Spirng 事务隔离级别

Spring 事务隔离级别

事务并发: 在java Spring中比较常见的就是 @Transactional 注解的方法被多个线程同时执行,或者多个 @Transactional 注解的方法操作相同的数据,并且这些方法被同时执行。

事务并发会出现三种比较常见的问题,分别是脏读不可重复读幻读

我们要在spring环境下验证以下问题时,有几个地方要特别注意
(1)junit中 @Transactional 是会自动回滚的,我们要在@Test 方法上加上 @Commint 注解
(2)mybatis有缓存机制,我们要关闭一级缓存,具体方法如下
在这里插入图片描述

假设有以下场景,小红和小明是夫妇,他们用的是同一张银行卡。银行卡中余额为1000元。

脏读

时间 小明 小红
t1 小明花费银行卡中的100元
t2 小红查询银行卡中的余额 900元
t3 小明支付失败,银行卡回滚到1000元

Spirng 事务隔离级别_第1张图片
实际中,这种操作可能会带来很严重的后入,如小明收到工资转账1000块,小红查询到余额有2000,小红用这2000块做了这个月的预算,然后小明公司转账失败,银行卡余额回滚,银行卡只有1000元。这个时候小红就会读到错误的数据,做出了错误的预算。

解决这个问题的方法就是 @Transactional(isolation = Isolation.READ_COMMITTED) 把小红的事务设置成读已提交。
设置后,只有小明执行了commit 或者 roback 后,小红才能感知到银行卡余额的变化。

不可重复读:

时间 小明 小红
t1 小明查询银行卡中有1000元
t2 小红花费了100元
t3 小红提交了事务
t3 小明查询银行卡中有900元
t3 小明提交了事务

Spirng 事务隔离级别_第2张图片
如果小明在两个事务中,分别查询银行卡余额,那么查询到金额不一样是合理的。但是如果小明在一个事务中,两次查询余额不一样则是不合理的。后者这种现象称为不可重复读。

解决这个问题我们可以设置 @Transactional(isolation = Isolation.REPEATABLE_READ) 来解决,这样一来,小明在同一个事务中只会多次查询也只会得到相同的余额。

幻读:

时间 小明 小红
t1 小明查询了所有银行卡
t2 小红办理了三张银行卡
t3 小红提交了事务
t3 小明查询了所有银行卡
t3 小明提交了事务

Spirng 事务隔离级别_第3张图片
Spirng 事务隔离级别_第4张图片
Spirng 事务隔离级别_第5张图片

小明在一个事务中,按照余额1000作为条件查询到的只有一张银行卡,小红提交事务之后,小明再去查询仍然只有一张,但是更新时缺更新两张银行卡的余额,这就快照读不存在幻读现象当前读存在幻读现象
同一个事务中select 后update可以简单理解为是 select for update ——>当前读

幻读和不可重复读的区别是前者以为单位,后者以位单位。

解决幻读可以通过 @Transactional(isolation = Isolation.SERIALIZABLE) 但是这样有一个严重的问题,小明提交事务之前,小红会一直堵塞,不能操作这张表,知道小明提交事务小红才可以操作这张表。会影响效率。

结论: spring 默认采用 @Transactional(isolation = Isolation.DEFAULT) 使用mysql时是REPEATABLE_READoracle 时是READ_COMMITTED

现象 READ_UNCOMMITTED READ_COMMITTED REPEATABLE_READ REPEATABLE_READ
脏读 × × ×
不可重复读 × ×
幻读 (当前√)(快照×) ×

附:演示项目
码云地址 mysql-isolation模块

你可能感兴趣的:(系统学习,java,mysql,数据库,spring)