Spring事务传播特性&MySQL事务四大隔离级别&脏读、幻读、不可重复读、MVCC

Spring事务&MySQL事务四大隔离级别

0、判断有没有@Transctional注解
1、利用事务管理器建立一个数据库连接conn

2, conn .autocommit = false  自动提交设为false
被代理的方法target.method(); 执行相应的sql

3、conn . commit() 成功提交,否则回滚 rollback():
 

事务特性ACID

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么一起失败。

  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。

  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

Spring 无事务的方法调用有事务的方法不会开启新的事务!Spring采用动态代理的机制来实现事务控制,动态代理最终调用原始对象,原始对象调用方法时不会触发动态代理

获取代理对象方法:

  • 使用ApplicationContext上下文获取

  • AopContext.currentProxy() 需配置启动类开启 @EnableAspectJAutoProxy(exposeProxy = true)

Spring事务传播特性(AOP)

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);
​
    private final int value;
​
    private Propagation(int value) {
        this.value = value;
    }
​
    public int value() {
        return this.value;
    }
}

事务隔离级别

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);
​
    private final int value;
​
    private Isolation(int value) {
        this.value = value;
    }
​
    public int value() {
        return this.value;
    }
}

传播属性 描述 补充说明
REQUIRED 如果存在事务,当前方法在这个事务中运行;如果不存在、开启自己的事务 需要、默认
SUPPORTS 如果存在事务,当前方法在这个事务中运行;如果不存在、非事务的支持 支持
MANDATORY 如果存在事务,当前方法在这个事务中运行;如果不存在则抛异常 必要的
REQUIRES_NEW 总是开启新的事务,如果主方法事务存在,则挂起
NOT_SUPPORTED 总是非事务执行,存在事务则挂起
NEVER 总是非事务执行,存在事务则抛异常 绝不
NESTED 如果有事务则嵌套,无则开启事务 嵌套

模拟 ——> 两个方法: 其中一个主方法,另一个子方法

方法头上加上注解、指定传播级别、数据库隔离级别

@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ)

Spring事务特点:

Spring的事务管理不需要与任何特定的事务API耦合(面向PlatformTransactionManager)

针对不同的持久层API,编程式事务提供一致的事务管理,通过模块化的一致性操作、管理事务;

MySQL并发问题

MySQL数据库隔离级别

隔离级别 脏读 不可重复读 幻读 是否默认 读取数据
读未提交 READ-UNCOMMIT 快照读和当前读:数据版本一样
读已提交 READ-COMMIT 总读取最新的快照版本
可重复读 REPEATABLE-READ 总读事务开启前的快照
序列化 SERIALIZABLE 最高隔离,保证ACID

可重复读 REPEATABLE-READ: 

读未提交 READ-UNCOMMIT: 事务B会读到事务A未提交的修改!

幻读解决: 使用间隙锁

间隙锁(锁某一范围的数据)

  • 间隙锁是一个在索引记录之间的间隙上的锁

  • 可重复读 REPEATABLE-READ默认使用

  • SQL语句使用WHERE关键字进行条件搜索,条件全部命中,加记录锁,否则,加间隙锁

  • 可能会导致死锁 (间隙锁之间不是互斥的,如果一个事务A获取到了(5,10]之间的间隙锁,另一个事务B也可以获取到(5,10]之间的间隙锁。这时就可能会发生死锁问题,如: 事务A获取到(5,10]之间的间隙锁不允许其他的DDL操作,在事务提交(间隙锁释放)之前,事务B也获取到了间隙锁(5,10],这时两个事务就处于死锁状态)

  • 间隙锁会用在非唯一索引或者不走索引的当前读中

  • 间隙的范围 根据检索条件向下寻找最靠近检索条件的记录值A作为左区间,向上寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。

假设你有一组记录,其中age列的数据为:

age[18,19,19,20,22,22,22,22,30,31]

select * from t where age=22;那么间隙锁锁定的间隙为:(20,30),所以你再想插入20到30之间的数就会被阻塞。执行update t set age = 22 where id = 1会被阻塞。

为什么写操作阻塞?

要保证每次查询age=22的数据行数不变,如果你将另外一条数据修改成了22,岂不会多了一条?

查看数据库的隔离级别

show variables like "tx_isolation"

Spring事务传播特性&MySQL事务四大隔离级别&脏读、幻读、不可重复读、MVCC_第1张图片

设置数据库的隔离级别

set tx_isolation = 'REPEATABLE-READ'

  • 脏读 (事务A、事务B同时执行)

1、 事务A更新了某条记录

2、 事务B读取了事务更新的记录

3、事务A回滚

4、事务B读取到的记录无效

  • 不可重复读

1、事务A读取了记录

2、事务B更新了事务A读取的记录,提交

3、事务A再次读取该记录(事务B修改的记录)

  • 幻读

事务A读取事务B新增的数据(不符合隔离性)

1、事务A读取了表里的一部分数据

2、事务B向同一张表中增加了新的行

  • 更新丢失

两个事务同时操作同一记录,后提交的事务会覆盖先提交的事务。

MVCC(MULTI-VERSION_CONCURRENCY_CONTROL) 多版本并发版本控制

MVCC对普通的SELECT不加锁 (常规SELECT * FROM TABLE 语句),如果读到的数据在执行DELETE或UPDATE,这时,读不会等锁释放,直接利用MVCC读取改行数据的历史快照,基于undolog实现,undo log用来做事务回滚。

普通的SELECT <—— 快照读也叫非阻塞读,即所谓快照读就是不加锁的非阻塞读!

SELECT * FROM TABLE FOR UPDATE; FOR UPDATE 实现一个排它锁,事务结束前,其他事务不能更新该数据!

隔离级别与代理对象

如果一个有事务的方法,内部再调用自己的另一个方法(方法被传播传播机制为NEVER),如果直接调用,不会报异常!此时要么拆成一个类,让该类的代理对象执行NEVER传播机制的方法。要么自己注入自己,自己执行自己的NEVER传播机制的方法,会报异常!

@AutoWired

private OrderService orderService;

你可能感兴趣的:(MySQL-RR-InnoDB,杂记和踩坑,mysql,Spring事务传播,动态代理,AOP)