Spring Transaction属性之Isolation

本文深入探讨了Spring Transaction的Isolation属性,通过实例解释了不同隔离级别如何影响数据库读现象,如Dirty Read和Phantom Read。文章还讨论了在并发场景下,当两个方法的事务隔离级别不同时可能出现的问题,并提到了Isolation的实现机制,如加锁和MVCC。

上一篇博客《Spring Transaction属性之Propagation》讲解了Propagation相关的知识,这篇博客主要关注于Isolation这个属性。


一、Isolation基础

请先移步Wikipedia:isolation.

Isolation Level VS Phenomena


上图来自wikipeida。

因为我们在申明Isolation Level时,我们仅仅关心什么Phenomena不会出现。所以,在SQL标准中,仅仅规定了Isolation Level会保证哪种读现象不会出现,而不会规定某种读现象一定会出现。

例如,在Isolation设置为Repeatable Read时,不会出现Dirty Read。但是可能会出现Phantoms。如在Postgres中,Repeatable Read的效果就等同于Serializable,而在其他的数据库,如DB2中,Repeatable Read会出现Phantom Read。


二、Isolation例子

Isolation本身前面的wikipedia的链接已经讲的很清楚了,这里我们用Spring Transaction来重现一个Phantom Read的情况。大家感受一下就行了。

假设我们有一张表叫User,User的id是主键。UserService提供了如下两个方法:


    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void insertData() {
        jdbcTemplate.update("insert into User (id, username, age) values (123, 'xxx', 19)");
        doSleep(20000);
    }


    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void readRange() {
        System.out.println(jdbcTemplate.queryForList("select * from User where uses > 18").size());
        doSleep(40000);
        System.out.println(jdbcTemplate.queryForList("select * from User where uses > 18").size());
    }


如果我们同时执行这两个方法:那么insertData中的SQL会在两次Query之间执行,于是readRange的两次结果就会相差一。如果将READ_COMMITTED改为了REPEATABLE_READ,则两次结果会相同。


问题:如果我们的两个方法的Transaction Isolation声明不同,我们应该“听”哪一个的呢?如上面的例子,我们把任意一个改成SERIALIZABLE后,会出现什么样的结果呢?


三、Isolation加强版

下面是何登成的博客,有些关于加锁与Isolation之间关系的分析,很值得一读:

http://hedengcheng.com/?p=771


四、Isolation的实现

常见的实现包括加锁和MVCC(Multi Version Consistency Control),更多的参考资料,移步:

http://coolshell.cn/articles/6790.html

http://en.wikipedia.org/wiki/Multiversion_concurrency_control

http://wiki.postgresql.org/wiki/Serializable

`@Transactional` 是 Spring 提供用来控制事务回滚/提交的注解,它可以将编程式事务转换为声明式事务,让开发者更多地关注业务逻辑的代码编写,使编码更加简单[^1][^2]。以下是 `@Transactional` 常见属性介绍: #### `propagation`(事务传播机制) 该属性用于定义事务的传播行为,即当一个事务方法被另一个事务方法调用时,如何管理事务。常见的传播行为有: - `Propagation.REQUIRED`:默认值,如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 - `Propagation.REQUIRES_NEW`:总是创建一个新的事务,如果当前存在事务,则将当前事务挂起。 - `Propagation.SUPPORTS`:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。 - `Propagation.NOT_SUPPORTED`:以非事务方式执行操作,如果当前存在事务,则将当前事务挂起。 - `Propagation.MANDATORY`:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 - `Propagation.NEVER`:以非事务方式执行,如果当前存在事务,则抛出异常。 - `Propagation.NESTED`:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。 示例代码: ```java @Transactional(propagation = Propagation.REQUIRED) public void method1() { // 业务逻辑 } ``` #### `isolation`(事务隔离级别) 该属性用于设置事务的隔离级别,即一个事务对数据的修改,在何时对其他事务可见。Spring 中事务隔离级别可以通过 `@Transactional` 中的 `isolation` 属性进行设置。常见的隔离级别有: - `Isolation.DEFAULT`:使用数据库默认的隔离级别。 - `Isolation.READ_UNCOMMITTED`:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读和幻读问题。 - `Isolation.READ_COMMITTED`:允许读取已经提交的数据,可以避免脏读,但可能会出现不可重复读和幻读问题。 - `Isolation.REPEATABLE_READ`:确保在同一个事务内多次读取同一数据的结果是相同的,可以避免脏读和不可重复读,但可能会出现幻读问题。 - `Isolation.SERIALIZABLE`:最高的隔离级别,完全服从 ACID 的隔离级别,确保不发生脏读、不可重复读和幻读问题,但会影响性能。 示例代码: ```java @Transactional(isolation = Isolation.READ_COMMITTED) @RequestMapping("/r3") public String r3(String name, String password) throws IOException { // 业务逻辑 return "r3"; } ``` #### `timeout`(超时时间) 该属性用于设置事务的超时时间,单位为秒。如果事务在指定的时间内没有完成,则自动回滚。 示例代码: ```java @Transactional(timeout = 5) public void method2() { // 业务逻辑 } ``` #### `readOnly`(只读事务) 该属性用于设置事务是否为只读事务。如果设置为 `true`,则表示该事务是只读的,不允许进行写操作,可以提高性能。 示例代码: ```java @Transactional(readOnly = true) public void method3() { // 业务逻辑 } ``` #### `rollbackFor` 和 `rollbackForClassName` `rollbackFor` 属性用于指定哪些异常类型会导致事务回滚,它接受一个异常类数组;`rollbackForClassName` 属性用于指定异常类名数组,当抛出这些异常时,事务会回滚。 示例代码: ```java @Transactional(rollbackFor = {Exception.class}) public void method4() throws Exception { // 业务逻辑 } ``` #### `noRollbackFor` 和 `noRollbackForClassName` `noRollbackFor` 属性用于指定哪些异常类型不会导致事务回滚,它接受一个异常类数组;`noRollbackForClassName` 属性用于指定异常类名数组,当抛出这些异常时,事务不会回滚。 示例代码: ```java @Transactional(noRollbackFor = {RuntimeException.class}) public void method5() { // 业务逻辑 } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值