Mysql 的脏读、不可重复读和幻读


Mysql 的脏读、不可重复读和幻读

数据库事务的隔离级别主要分为未提交读、读已提交、可重复读以及串行化

事务的隔离级别

事务有不同的隔离级别,每种隔离级别对于 select 语句的执行有不同的影响

在测试事务的隔离级别之前,先创建一张测试表,用于测试事务的隔离级别


create table student(
  id integer primary key auto_increment,
  name varchar(40) comment '姓名',
  age varchar(2) comment '年龄'
);

insert into student (name, age) values ('g5niusx','18');
insert into student (name, age) values ('biezhi','18');

通过执行 SELECT @@GLOBAL.transaction_isolation, @@GLOBAL.transaction_read_only; 可以查看 mysql 的事务隔离级别 通过执行 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 可以设置 mysql 的事务隔离级别为 READ UNCOMMITTED

以上的语句是 mysql8.0 的版本

READ UNCOMMITTED(未提交读)

当事务的隔离级别设置为 READ UNCOMMITTED 的时候,事务中还没有提交的数据,可以被其他的事务提前获取到,称之为未提交读。造成的影响是发生脏读

假设 A 用户对 student 表做了一次更新 update student set age = '14' where name = 'biezhi';,另外一个 B 用户恰好在 A 的更新未提交的时候对这条数据做了一次查询, 那么 B 用户查到的 age 的值会变成14,也就是 A 用户还没有提交的数据,被其他用户获取到了还没有提交的数据

READ COMMITTED(读已提交)

当事务的隔离级别设置为 READ COMMITTED 的时候,会解决脏读的问题。但是会出现新的问题,有可能会发生幻读和不可重复读

当 A 用户开启一个事务(transaction_a)对 student 表做了一次统计查询 select count(0) from student;,会返回一个结果。在 transaction_a 未提交的时候, B 用户开启了一个事务 transaction_b 对 student 表新增了一条数据,然后提交了 transaction_b。 在 transaction_a 中再做一次查询,就会发现数量发生了变化。 这种现象就是幻读

当 A 用户开启一个事务(transaction_a)对 student 表做了一次查询 select * from student where name = 'g5niusx';,会返回一个结果。当 transaction_a 还没有提交的时候, B 用户开启了一个事务(transaction_b),对 student 表的数据做了一次更新 update student set age = '20' where name = 'g5niusx'; 然后提交了 transaction_b 这个时候,A 用户在 transaction_a 再次做了一次查询的时候,返回的结果就会变成 B 用户更新的结果,很明显在同一个事务 (transaction_a) 里面查询的数据却不一样,这种现象就是不可重复读。

幻读的重点是在于新增的数据,指的是读到了其他事务新增的数据

不可重复读

REPEATABLE READ(可重复读)

mysql 默认的事务隔离级别,防止了脏读,不可重复度的问题,但是没有办法防止幻读的问题。

当 A 用户开启一个事务(transaction_a)对 student 表做了一次查询 select * from student where name = 'g5niusx';,会返回一个结果。当 transaction_a 还没有提交的时候, B 用户开启了一个事务(transaction_b),对 student 表的数据做了一次更新 update student set age = '20' where name = 'g5niusx'; 然后提交了 transaction_b 这个时候,A 用户在 transaction_a 再次做了一次查询的时候,返回的结果还是之前的结果。

不可重复读解决

可重复读的隔离级别解决了脏读,不可重复读的问题,但是没有办法解决幻读的问题。

SERIALIZABLE(串行化)

mysql 最高级别的隔离级别,将所有的事务串行化执行,解决了脏读,不可重复读,幻读。但是性能底下,一般不会采用这种隔离级别。

  #code 

« 关于数据库 ACID 和三大范式的通俗解释 从零 Redis 系列 -- 安装与运行 »
blog comments powered by Disqus