隔离级别
MySQL的隔离级别分为四个级别分别是:
*读未提交(READ-UNCOMMITTED):允许在事务中读取到其他事务尚未提交的数据,会导致脏读
*读已提交(READ-COMMITTED):允许读取并发事务已经提交的数据,可以阻止脏读脏读,但可能会出现幻读和不可重复读。
*可重复读(REPEATABLE-READ):同一次事务中反复读取到的数据不会发生变化,除非数据是被当前事务修改,可以阻止脏读和不可重复度,但还可能出现幻读
*可串行化(SERIALIZABLE):隔离程度最高,完全符合ACID的隔离级别。各个事务按序执行,事务之间不会产生干扰,可以完全避免脏读、不可重复读以及幻读。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 | √ | √ | √ |
读已提交 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |
MySQL的InnoDB引擎默认的隔离级别是可重复读,Mysql8.0以下版本可以通过select @@tx_isolation; 查看隔离级别,Mysql8.0可以通过select @@transaction_isolation;查看隔离级别
实际演示
幻读、不可重复读和脏读知识
脏读、不可重复读和幻读都是数据库中事务并发控制的问题,是由于多个事务同时对同一数据进行操作而导致的数据不一致性问题。下面分别介绍一下这三种问题的定义和解决方案。
- 脏读(Dirty read) 脏读是指一个事务读取了另一个事务未提交的数据。在一个事务对某条数据进行修改时,如果另一个事务读取了该数据并且使用了这个未提交的数据,那么这个未提交的数据就是脏数据。如果第一个事务回滚了该修改,那么第二个事务所读取的数据就是不正确的。 解决方案:使用锁来避免脏读的发生。在一个事务对某条数据进行修改时,可以对该数据进行独占锁定,直到该事务成功提交或者回滚后才释放锁。另外,数据库也提供了一些隔离级别,如Read Committed和Serializable,可以设置事务的隔离级别,进一步保证数据的一致性和正确性。
- 不可重复读(Non-repeatable read) 不可重复读是指在同一个事务中,多次读取同一条数据,但是在这些读取过程中,该数据发生了变化。例如,在一个事务中读取了某条数据,并且在此之后另一个事务修改了该数据并提交了事务,那么在第一个事务中再次读取该数据时,读取到的数据就不同了。 解决方案:使用锁或者MVCC(多版本并发控制)来避免不可重复读的发生。在使用锁的情况下,可以对该数据进行共享锁定,直到第一个事务提交或回滚后才释放锁。在使用MVCC的情况下,每个事务读取的数据都是其自己的版本,不会受到其他事务的修改影响。
- 幻读(Phantom read) 幻读是指在同一个事务中,多次执行同一条查询语句,但是在这些执行过程中,该查询语句返回的数据集合发生了变化。例如,在一个事务中执行了一个SELECT语句,返回了一些数据,并且在此之后另一个事务修改了数据并提交了事务,那么在第一个事务中再次执行该SELECT语句时,返回的数据集合就不同了。 解决方案:使用锁或者MVCC和一些特殊的查询方式来避免幻读的发生。在使用锁的情况下,可以对查询的数据进行共享锁定,直到第一个事务提交或回滚后才释放锁。在使用MVCC的情况下,可以使用一些特殊的查询方式,如Serializable隔离级别,来避免幻读的发生。
读未提交
先在事务1中设定隔离级别未读未提交
set session transaction isolation level read uncommitted;
再在事务1中开启事务
start transaction;
在事务1中查询表student数据
select * from student;
在事务2中开启事务
start transaction;
在事务2中修改数据
update student set name='zhangsan' where id=1;
事务2的事务先不提交
重新在事务1中查询一次
select * from student;
可以发现我们在事务1中查询到了事务2中修改但没有提交的数据。这就是脏读了。
脏读(Dirty read)是指在数据库中,一个事务正在访问一条数据并且对其进行了修改,但是此时该事务还没有提交,这时另一个事务也访问了同一条数据并且读取了它,然后这个事务就会读取到未提交的、脏的数据。如果此时第一个事务进行了回滚,那么第二个事务所读取的数据就是不正确的。 脏读可能导致数据的不一致性和错误,因此应该尽量避免。在数据库中,可以使用锁来避免脏读的发生。例如,在一个事务对某条数据进行修改时,可以对该数据进行独占锁定,直到该事务成功提交或者回滚后才释放锁,这样可以避免其他事务读取到脏数据。另外,数据库也提供了一些隔离级别,如Read Committed和Serializable,可以设置事务的隔离级别,进一步保证数据的一致性和正确性。
此时我们应该在事务2中回滚
rollback;
我们再次在事务1中查询一遍
select * from student;
我们会发现数据变回来了,说明刚刚是脏数据
读已提交
先在事务1中设定隔离级别未读未提交
set session transaction isolation level read committed;
再在事务1中开启事务
start transaction;
在事务1中查询表student数据
select * from student;
在事务2中开启事务
start transaction;
在事务2中修改数据
update student set name='zhangsan' where id=1;
在事务2提交前在事务1中查询
select * from student;
我们发现数据没有发生变化
事务2的事务提交
commit;
重新在事务1中查询一次
select * from student;
可以发现我们在事务1中查询到了事务2中修改提交的数据。这样避免了脏读。
可重复读
先在事务1中设定隔离级别未读未提交
set session transaction isolation level repeatable read;
再在事务1中开启事务
start transaction;
在事务1中查询表student数据
select * from student;
在事务2中开启事务
start transaction;
在事务2中修改数据
update student set name='zhangsan' where id=1;
在事务2提交前在事务1中查询
select * from student;
我们发现数据没有发生变化
事务2的事务提交
commit;
重新在事务1中查询一次
select * from student;
可以发现我们在事务1中查询不到了事务2中修改提交的数据。这样避免了脏读和不可重复读。
幻读
先在事务1中设定隔离级别未读未提交
set session transaction isolation level read committed;
再在事务1中开启事务
start transaction;
在事务1中查询表student数据
select * from student;
在事务2中开启事务
start transaction;
在事务2中修改数据
update student set name='zhangsan' where id=1;
在事务2提交前在事务1中查询
select * from student;
我们发现数据没有发生变化
事务2的事务提交
commit;
重新在事务1中查询一次
select * from student;
可以发现我们在事务1中查询到了事务2中修改提交的数据。这样避免了脏读。
可重复读
在事务1中开启事务
start transaction;
在事务1中查询表student数据
select * from student;
在事务2中开启事务
start transaction;
在事务2中插入数据
insert into student(id,name,gender,age) values(5,'zhangsan','F',22);
在事务2提交前在事务1中查询
select * from student;
我们发现数据没有发生变化
事务2的事务提交
commit;
重新在事务1中查询一次
select * from student for update;
使用当前读我们发现我们在事务1中读取到了事务2提交的数据,事务1第二次读取的结果多了数据这就是幻读。