浅析Mysql隔离级别
Posted 雪洗中关村
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析Mysql隔离级别相关的知识,希望对你有一定的参考价值。
from: www.jianshu.com/p/db334404d909
一、Mysql的四个隔离级别
预备工作:
-
先创建一个test数据库及account表
create database test; use test; create table account( id int not null, balance float not null, PRIMARY KEY ( id) ) INSERT INTO table(id,balance) VALUES (1,1000); INSERT INTO table(id,balance) VALUES (2,1000);
开启两个控制台窗口,当做两个用户(A和B)
1.1. read unCommited (未提交读)
也即RU,在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
A用户操作如下:
set session transaction isolation level read uncommitted; start transaction; select * from account;
结果如下:
id balance
1 1000
2 1000
B用户操作如下:
set session transaction isolation level read uncommitted; start transaction; update account set balance=balance+200 where id = 1;
随后在A用户终端中查询数据,结果如下:
id balance
1 1200
2 1000
可以看到B用户并未提交事务,但是A用户却能读到未提交的数据,这就是脏读。
1.2. read commited (提交读)
即RC,大多数数据库系统为了避免 脏读 采用的默认隔离级别都是read commited(但mysql不是,Mysql的默认隔离级别是repeatable read )。
READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能”看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。
这个级别有时候叫做不可重复读(nonrepeatble read),因为两次执行同样的查询,可能会得到不一样的结果。以例子说明:
我们将用户B所在的会话当前事务隔离级别设置为read commited。
set session transaction isolation level read committed;
在A所在的会话中执行
update account set balance=balance-200 where id = 1;
在B用户的会话中查询:
select * from account;
结果如下:
id balance
1 1000
2 1000
发现数据没有变,还是1000,说明可以避免脏读了。
接着A用户会话中将事务提交:
commit;
再次在B中查询,结果如下:
id balance
1 800
2 1000
可以看到,B用户读取到了A用户提交的数据。这么做有什么问题么?那就是我们在会话B同一个事务中,读取到两次不同的结果。这就造成了不可重复读,就是两次读取的结果不同。
1.3 . repeatable read(可重复读)
REPEATABLE READ解决了脏读的问题。该隔离级别保证了在同一个事务中多次读取同样记录结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。
所谓 幻读,指的是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。
Mysql的RR是由“行排它锁+MVCC”一起实现的。
我们将用户B所在的会话当前事务隔离级别设置为repeatable read。
set session transaction isolation level repeatable read; start transaction;
接着在B中查询数据:
id balance
1 1000
2 1000
两条。
然后我们到A用户会话中插入一条数据:
insert into account(id,balance) value(3,1000);
在A中查看是否添加成功:
————————————————————————
insert into account(id, balance) value (3, 1000);
select * from account;
id balance
1 1000
2 1000
3 1000
————————————————————————
成功,有三条数据。
回到用户B会话中,再次查询:
id balance
1 1000
2 1000
发现没有变还是两条。这时,用户B想插入一条id=3,balance=1000的数据:
insert into account(id,balance) value(3,1000);
会报错:
————————————————————————————————
mysql> insert into account(id,balance) value(3,1000);
ERROR 1062(23000): Duplicate entry ‘3‘ for key ‘primary‘
mysql>
————————————————————————————————
说是主键重复了,可是B用户刚刚查询并没有id=3的记录。这就是幻读现象。
我的理解是:不可重复读指的是update操作,而幻读指的是insert或delete操作。
1.4 SERIALIZABLE(串行化)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取每一行数据都加锁,所以可能导致大量的超时和锁争用问题。
实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
以上是关于浅析Mysql隔离级别的主要内容,如果未能解决你的问题,请参考以下文章
浅析SQL Server在可序列化隔离级别下,防止幻读的范围锁的锁定问题
浅析SQL Server在可序列化隔离级别下,防止幻读的范围锁的锁定问题