MySQL的四个隔离级别
预备工作:
先创建一个student表并在其中创建两条数据
select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | roy |
| 2 | davina |
+----+--------+
2 rows in set
开启两个控制台窗口,当做两个用户(AB)
READ UNCOMMITTED(未提交读)
当数据库中是这个隔离级别的时候,事务中的修改,即使没有提交,对于其他事务来说也是可见的是。即当A事务修改了某一条记录但是还没有提交,这个时候B事务过来了,B事务就会读到A事务还未提交的数据。事务可以读取未提交的数据,这也就是我们说的脏读(Dirty Read)。这个隔离级别会导致很多问题,所以我们一般并不会采用这个隔离级别。
A用户操作如下
查询当前表中的所有数据结果如下
set session transaction isolation level read uncommitted;
start transaction;
select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | roy |
| 2 | davina |
+----+--------+
2 rows in set
B用户操作如下
B操作将id为1的数据name修改,但是这时候并没有提交操作
set session transaction isolation level read uncommitted;
start transaction;
update student set name='book' where id = 1;
随后我们在A终端中查询数据,结果如下
set session transaction isolation level read uncommitted;
start transaction;
select * from student;
Query OK, 0 rows affected
Query OK, 0 rows affected
+----+--------+
| id | name |
+----+--------+
| 1 | book |
| 2 | davina |
+----+--------+
2 rows in set
可以看到这个时候产生了脏读现象。A用户读到了B用户修改之后但是未提交的数据
READ COMMITTED(提交读)
大多数的数据库默认的隔离级别(需要注意的是MySQL默认的隔离级别是REPEATABLE READ)。
简单的定义就是:一个事务开始知道提交之前,多做的任何操作对于其他事务均是不可见的。这个级别有时候叫做不可重复读(nonrepeatble read),因为两次执行同样的查询,可能会得到不一样的结果。
我们将用户A所在的会话的事务隔离级别设置为read committed
set session transaction isolation level read committed;
Query OK, 0 rows affected
在B中修改一条数据
update student set name='key' where id=1;
Query OK, 1 row affected
Rows matched: 1 Changed: 1 Warnings: 0
在A中查询数据
select * from student;
Query OK, 0 rows affected
Query OK, 0 rows affected
+----+--------+
| id | name |
+----+--------+
| 1 | book |
| 2 | davina |
+----+--------+
发现数据并没有变化,说明可以避免脏读了
接着B用户会话将事务提交
commit;
再次查询B中结果
select * from student;
Query OK, 0 rows affected
Query OK, 0 rows affected
+----+--------+
| id | name |
+----+--------+
| 1 | key |
| 2 | davina |
+----+--------+
可以看到A用户读取到了B用户提交的数据,这样还有一个问题就是A
事务在两次读取中读取到的结果是不同的。这就出现了不可重复读,也就是说两次读取的结果不同
REPEATABLE READ(可重复读)
可重复读隔离级别保证了同一事务在多次读取同样相同记录结果的时候是一致的。但是理论上,可重复读级别还是没有解决另一个幻读的问题。
什么是幻读:当某个事务读取某个范围内的记录是,另一个事务又在该范围内插入或者删除了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。
首先我们查询A用户当前的数据
select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | key |
| 2 | davina |
+----+--------+
2 rows in set
在B中插入一条新的记录,并查询
insert into student(id,name)value (3,'jack');
select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | book |
| 2 | davina |
| 3 | jack |
+----+--------+
3 rows in set
在A中查询
select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | key |
| 2 | davina |
+----+--------+
2 rows in set
发现并没有添加任何数据,还是只有两条数据。如果这时候我们在A中插入一条id为3的数据
insert into student(id,name)value (3,'jack');
说是主键重复了,可是A用户刚刚查询并没有id=3的记录。这就是幻读现象。
SERIALIZABLE(串行化)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取每一行数据都加锁,所以可能导致大量的超时和锁争用问题。
在我们实际的开发应用中很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受系统没有并发情况的出现,才会考虑使用该级别。