mysql 幻读

ddatsh

dev #mysql

InnoDB 引擎通过什么技术保证事务ACID特性


InnoDB 默认隔离级别虽然是「可重复读」,但很大程度上避免幻读现象(并不是完全解决),解决方案有两种:

CREATE DATABASE test;
USE test;
CREATE TABLE t_stu ( id BIGINT PRIMARY KEY auto_increment, NAME VARCHAR ( 32 ), score INT );

insert into t_stu (id,name,score)values (1,'小林',50),(2,'小明',60),(3,'小红',70),(4,'小蓝',80);

幻读场景

# 事务 A
begin;
select * from t_stu where id = 5;

事务 B 插入一条 id = 5 的记录,并且提交了事务

begin;
insert into t_stu values(5, '小美', 18);
commit;

事务 A 更新 id = 5 这条记录(事务 A 看不到 id = 5 这条记录,但是去更新了这条记录),然后再次查询 id = 5 的记录,事务 A 就能看到事务 B 插入的纪录了,幻读就是发生在这种违和的场景

update t_stu set name = 'bad' where id = 5;
select * from t_stu where id = 5;

前后两次 select * from t_stu where id = 5; 结果集不一致,发生幻读

可重复读隔离级别下,事务 A 第一次执行普通的 select 语句时生成了一个 ReadView,之后事务 B 向表中新插入了一条 id = 5 的记录并提交。接着,事务 A 对 id = 5 这条记录进行了更新操作,在这个时刻,这条新记录的 trx_id 隐藏列的值就变成了事务 A 的事务 id,之后事务 A 再使用普通 select 语句去查询这条记录时就可以看到这条记录了,于是就发生了幻读

要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select … for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录