事务隔离级别:可重复读

MySQL [pom_5]> select @@global.tx_isolation;

+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+

如果事务隔离级别显示REPEATABLE-READ,即是可重复读。

事务的四种隔离级别

(引自: Innodb中的事务隔离级别和锁的关系)

在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。我们的数据库锁,也是为了构建这些隔离级别存在的。

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可 不可 可能
可串行化(Serializable ) 不可能 不可能 不可能

未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)

可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读

串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

可重复读和幻读

在可重复读中,该sql第一次读取到数据后,就将这些数据加锁(悲观锁),其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。

但是MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来实现。

实际测试结果

对相同db实例创建两个连接:a和b,来测试select, select for update, update语句的生效情况

不使用select for update行级锁

a先提交 b先提交
a先开启事务 b的update内容 a的update内容
a先开启事务 b的update内容 a的update内容

结果是谁最后提交,谁的结果生效

a先使用select for update, update行级锁

a使用select for update a使用select for update a使用update
a开启事务,b不开启 b里面的select for update和update被阻塞 b不受影响 b里面的select for update和update被阻塞
a不开启事务,b开启 b不受影响 b不受影响 b不受影响
a,b开启事务 b里面的select for update和update被阻塞 b不受影响 b里面的select for update和update被阻塞
a,b不开启事务 b不受影响 b不受影响 b不受影响

结果是只有使用begin显式开启事务时,使用select for update才会对数据加上行级锁,对其他连接的的select for update(对select没有影响)和update造成阻塞效果。

a上锁后修改提交,b读取到的内容

下面是a开启事务,然后使用a使用select for update,接着b使用select for update/select,a执行updat,最后commit,观察整个过程中b的查询效果。

b使用select for update b使用select
b开启事务 b里面的select for update被阻塞,a提交后,b读到的是a修改的内容 b不受a阻塞,读到的仍是b事务开启时的内容
b不开启事务 b里面的select for update被阻塞,a提交后,b读到的是a修改的内容 b不受a阻塞,读到的数据在a提交后变化

在连接a使用begin开启事务之后,select for update和update对连接b的阻塞效果

a先执行select for update a先执行update
b后执行select 未阻塞 未阻塞
b后执行select for update 阻塞 阻塞
b后执行update 阻塞 阻塞
posted @ 2018-04-19 00:58  catmelo  阅读(45999)  评论(4编辑  收藏  举报