Mysql事务隔离水平(Isolation Levels)简介
Mysql有4种事务隔离水平(Transaction Isolation Levels)
1.未提交读(Read Uncommitted):一个事务可能读取到其他会话中未提交事务修改的数据,就是脏读(dirty read)。这种隔离水平是没有实际意义的。
2.提交读(Read Committed):从字面理解的意思是事务只能读取到已经提交的数据。在High Performance Mysql一书的解释是:
引用
a transaction will see only those changes made by transactions that were already committed when it began, and its changes won’t be visible to others until it has committed.
单纯从字面上还是有点绕口。其实比较简单,就是一个事务里面如果有对一个数据的多次访问,Read Committed不保证每次访问的结果都一样。如果别的会话在该事务2次访问数据之间对该数据进行了修改或者删除,2次访问就会得到不一样的结果。
所以这个隔离水平也叫nonrepeatable read(不可重复读)。
Oracle缺省使用这个隔离水平。
3. 可重复读(Repeated Read):可重复读,结合上面对提交读的解释,这个隔离水平就很好理解。一个事务里面如果有对一个数据的多次访问,Repeated Read会保证在该事务内所有访问结果都一致,不管其他会话是否尝试对该数据修改或者删除。
Mysql的InnoDB缺省使用这个隔离水平。
可重复读可能会带来幻读(phantom read)的问题。幻读是指一个事务里面如果有对一个数据的多次访问,后一次的访问读到别的会话插入的数据。(别的会话刚好插入一个数据满足改访问的where条件)。
但是mysql innodb通过multiversion concurrency control技术保证了不会发生幻读的情况。
4. 串行(Serializable):串行化访问,每次读都需要获得表级共享锁,读写相互都会阻塞。串行的最大问题的是并发能力非常低。
--------------------华丽的分割线---------------------------
为了更好的区别Read Committed和Repeated Read,读者可以参考这个例子:
1) 首先建立一个表:
CREATE TABLE t1(id int,value int);
INSERT t1 VALUES(1,1);
2) 测试Read Committed会出现不可重复读的情况
同时打开两个的会话,在第一个会话执行以下的sql:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 设置Read Committed级别
START TRANSACTION;
SELECT * FROM t1;
select sleep(10); -- 休眠10秒,等待另一个会话修改数据
SELECT * FROM t1;
commit ;
在第一个会话休眠期期间,在第二个会话执行以下的SQL:
update t1 set value = 2 where id =1;
第一个会话结束后,可以看到两个SELECT的结果是不一样的,第二次select被第二个会话的数据修改影响了。
3)测试Repeated Read的可重复读能力
在会话一执行以下SQL:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 设置 repeatable read级别
START TRANSACTION;
SELECT * FROM t1;
select sleep(10); -- 休眠10秒,等待另一个会话修改数据
SELECT * FROM t1;
Commit ;
在第一个会话休眠期期间,在第二个会话执行以下的SQL:
update t1 set value = 3 where id =1;
第一个会话结束后,可以看到两个SELECT的结果是完全一样的,而且第二个会话没有被阻塞,并不是串行的读写来保证可重复读。