mysql 四种隔离级别详解

本文以一系列详细的sql语句,使你彻底理解mysql的四种隔离级别。
下面的例子中,用到了两个会话(session),每个session中各开启了一个事务,分别记作session A, 事务A;session B, 事务B。

准备工作

首先新建一个表‘student’,并插入两行数据

CREATE TABLE `student` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

insert student(id, name) values(1, 'zhangsan'), (2, 'lisi');

表中数据如下图:

id name
1 zhangsan
2 lisi

然后用以下sql查询数据库的默认隔离级别

select @@transaction_isolation; 

可以看到默认隔离级别是“可重复读”

REPEATABLE-READ

接下来通过sql详细地说明每一个隔离级别的含义。

read-uncommitted

验证“读未提交”, 首先我们开启一个会话(session A),
设置数据库的隔离级别为’read-uncommitted’。

set session transaction_isolation = 'read-uncommitted';

开启事务A

start transaction;

更新name,并查询student表

update student set name = 'hehe';
select * from student;

结果如下:

id name
1 hehe
2 hehe

注意,当前事务A中,我们还未提交以上update语句的修改(start transaction之后,需要手动执行commit,才是提交)
现在我们另开一个会话(session B),并设置隔离级别为"读未提交"。

set session transaction_isolation = 'read-uncommitted';

接下来开启事务B,并查询表’student’

start transaction;
select * from student;

结果如下

id name
1 hehe
2 hehe

此时,事务A和B均未提交,我们在事务B中查到了事务A中未提交的数据,此为脏读

read-committed

接下来验证“读已提交”, 首先提交以上未提交的事务A和事务B(执行commit;),并设置session A和session B的隔离级别为“读已提交”:

set session transaction_isolation = 'read-committed';

在session B中开启事务B,并查询表’student’

start transaction;
select * from student;

结果如下:

id name
1 hehe
2 hehe

在session A中开启事务A, 并更新student

start transaction;
update student set name = 'zhangsan' where id = 1;

事务A未提交的时候,再次在事务 B中查询student

select * from student;

结果与上面一样:

id name
1 hehe
2 hehe

说明“读已提交”避免了脏读。
事务A中执行提交

commit;

事务B中再次执行查询student

select * from student;

结果如下:

id name
1 zhangsan
2 hehe

此时,事务A已提交,事务B未提交。而事务B能读到事务A已提交的数据,并且和前面一次查询的结果不同了,此为不可重复读。“读已提交”已经可以避免脏读问题。

repeatable-read

接下来验证“可重复读”。首先提交上面的事务A和事务B,然后设置session A和session B的隔离级别是“可重复读”:

set session transaction_isolation = 'repeatable-read';

session B中,开启事务B并查询student

start transaction;
select * from student;

结果如下:

id name
1 zhangsan
2 hehe

session A中开启事务A,并插入一条数据

start transaction;
insert student (id, name)values(3, 'haha');

此时事务A未提交,在事务B中插入同样id为3的一行数据

insert student (id, name)values(3, 'haha');

报错如下:

[Err] 1062 - Duplicate entry '3' for key 'PRIMARY'

再次查询student

select * from student;

还是

id name
1 zhangsan
2 hehe

可以看出,事务B中查询student,只有两行数据(id是1和2),可是插入id是3的数据,却报错说已存在id为3的数据,此为幻读。“可重复读”已经可以避免不可重复读问题。

serializable

这个就不验证了,很容易理解,“串行化”可以避免幻读问题。

总结

隔离级别 脏读 不可重复读 幻读
read-uncommitted 1 1 1
read-committed 0 1 1
repeatable-read 0 0 1
serializable 0 0 0

特别说明,不可重复读幻读的区别不是太明显,不可重复读是指一个事务读到了另一个事务提交的修改,幻读是指一个事务感受到了另一个事务的增删数据。也就是说前者指的是“增删改查”中的“改”,后者指的是“增删”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章