平时开发中我们可能只听过各个隔离级别下会发生什么问题,但是可能并没有遇到过或者自己实现过。下面是用代码来实现MySQL各个隔离级别下发生的问题。
MySQL隔离级别
- 读未提交(RU)(Read Uncommitted):在该级别下,没有任何正确性保证,任何事务都可以看到其他事务未提交的写操作,可能发生严重的脏读、幻读、不可重复读的问题。由于这种级别的问题较多,一般不会使用。
- 读已提交(RC)(Read Committed):在该级别下,一个事务只能看到已被另一个事务提交的更新过的行。避免了脏读的问题,但可能出现不可重复读和幻读的问题。
- 可重复读(RR)(Repeatable Read):在该级别下,一个事务多次读取同一范围的数据(包括在查询条件下的新数据插入)时,能看到相同的行,支持多版本并发控制(MVCC)。这种级别下会避免脏读和不可重复读,但程序仍然可能会发生幻读。
- 串行化(Serializable):在该级别下,所有事务都互相完全看不到对方的修改,所有事务的执行结果看起来就像是串行执行一样。这种隔离级别能避免所有的并发问题,包括脏读、不可重复读和幻读,但缺点是会大幅降低系统的并发性能。
以下是我的建表语句,并且初始化一些数据
create table user
(
id bigint auto_increment comment '主键ID'
primary key,
age int(2) not null comment '年龄',
name varchar(10) null comment '姓名'
);
INSERT INTO mmall_test.user (id, age, name) VALUES (1, 20, 'Gary');
INSERT INTO mmall_test.user (id, age, name) VALUES (2, 20, 'Jack');
INSERT INTO mmall_test.user (id, age, name) VALUES (3, 20, 'Tom');
INSERT INTO mmall_test.user (id, age, name) VALUES (4, 20, 'Sandy');
INSERT INTO mmall_test.user (id, age, name) VALUES (5, 20, 'Billie');
初始数据
1、读未提交(脏读)
- 首先将数据库设置成RU级别
set session transaction isolation level read uncommitted;
-
输入
select @@session.tx_isolation;
查看当前隔离级别
-
然后打开两个控制台,分别输入
begin;
同时开启事务 -
在事务1插入一条数据
insert into user(id, age, name)VALUES (6,25,'Bob');
不提交 -
事务2输入
select * from user;
以下是输出结果
可以发现事务2读到了事务1未提交的脏数据,这就是发生了脏读。
2、读已提交(不可重复读)
- 先将隔离级别设为RC
set session transaction isolation level read committed;
- 事务1输入以下sql,可以发现事务1输出插入的新数据
begin;
insert into user(id, age, name)VALUES (6,25,'Bob');
select * from user;
- 事务2进行查询,可以发现并没有读到事务1未提交的数据,避免了脏读现象
- 提交事务1之后,事务2不提交继续执行查询操作,可以发现竟然读到了事务1已经提交过的数据,这样两次查询结果不同就被称为不可重复读。
3、可重复读(幻读)
- 先将隔离级别设为RR
set session transaction isolation level repeatable read;
- 还是用刚刚的例子,事务1和事务2同时开启,在事务1执行插入操作并提交成功后,事务2进行查询,可以发现事务2读到的数据始终是一样的,这样避免了不可重复读
- 事务2不提交事务,继续执行修改语句修改id=6的数据,正常来说我们查出来的数据是5条,不会修改成功,接下来神奇的事情发生了。我们发现修改成功了并且再次执行查询语句竟然多出来我们修改之后的数据。感觉出现幻觉了,这种现象我们称为幻读。
补充:有的小伙伴可能会说MVCC不是可以解决幻读吗,这种说法其实是不准确的。MVCC只能在快照读的情况下解决幻读问题,在当前读的情况下是不能解决幻读问题的。正常的
select * from user;
是快照读,如果我们在上面的例子上使用select * from user for update;
当前读的方式是不可以解决幻读的,需要搭配上间隙锁来解决。
4、串行化
- 将隔离级别设为Serializable
set session transaction isolation level serializable ;
- 开启两个事务后,我们会发现事务中任何一个操作都是加锁的,其他事务是有等待当前事务提交后才能进行操作,所以不存在脏读、不可重复读、幻读问题。