非唯一索引列num
示例1:
事务1 可重复读&事务手动提交 | 事务2 可重复读&事务手动提交 |
---|---|
start transaction; | |
start transaction; | |
select * intest where num=5 for update;(结果只有一条) | |
insert into intest set name=‘teat’, num=5;(此处会阻塞住) | |
select * intest where num=5 for update;(结果仍只有一条,不会幻读) |
可重复读隔离级别中
1、select是快照读,不会出现幻读的情况;
2、select for update 是当前读,会加排他锁,其它事务不能修改,不会出现幻读
示例2:
事务1 可重复读&事务手动提交 | 事务2 可重复读&事务手动提交 |
---|---|
start transaction; | |
start transaction; | |
update intest set num=6 where id=6; | |
delete from intest where id=6;(此处会阻塞住) | |
select * from intest where id=6;(id等于6的记录不会被删除) |
可重复读隔离级别中
1、update 会自动加锁,堵塞其它事务,不会出现幻读;
示例3:
事务1 可重复读&事务手动提交 | 事务2 可重复读&事务手动提交 |
---|---|
start transaction; | |
start transaction; | |
delete from intest where id=17; | |
commit; | |
update intest set num=88 where id=17;(看下面的查询结果,id=17的记录并没有更新) | |
commit; | |
由于mvcc的快照的原因,事务2中delete掉id=17的记录提交后,事务1再update这条记录,update是当前读,因此就更新不到被删除的记录;接着再select出数据,显示id=17的记录并没有被删除掉,是因为该条记录的trx_id隐藏列的值还是事务1的事务id,并没有改变,因此仍然可以查出来。
示例4:
事务1 可重复读&事务手动提交 | 事务2 可重复读&事务手动提交 |
---|---|
start transaction; | |
start transaction; | |
update intest set num=6 where id=6; | |
delete from intest where id=6;(此处会阻塞住) | |
select * from intest where id=6;(id等于6的记录不会被删除) |
可重复读隔离级别中
1、update 会自动加锁,堵塞其它事务,不会出现幻读;
示例5:
事务1 可重复读&事务手动提交 | 事务2 可重复读&事务手动提交 |
---|---|
start transaction; | |
start transaction; | |
insert into intest set name=‘test123’,num=5; | |
commit; | |
update intest set num=19; | |
可重复读中
1、可以看到,mvcc并没有完全解决幻读的问题,当事务2插入一条新的数据并提交后,事务1在查询并没有出现新的数据。但是当事务1update intest set num=19;之后在执行查询就出现了新当数据,因此出现了幻读。
事务1之所以会出现“幻读”,是因为先进行了一次快照读(select),读取了历史数据,再进行了一次当前读(update intest set num=19),读取最新数据,这样当然会出现幻读了。
需要注意的是,因为update intest set num=19更新到了示例5中的事务2插入的id=46的数据,所以该示例中的事务1在update intest set num=19后select会查询出事务2插入的数据,也就是幻读。如果事务1的update的操作没有更新到事务2插入的数据,那么select是不会出现幻读的
如果事务中都使用快照读,那么就不会产生幻读现象,但是快照读和当前读混用就会产生幻读。
解决这个幻读的办法:可以在select的时候加上锁,这样其它事务就不能插入数据,这样即使事务1执行update或者insert等当前读操作的语句后也不会有新的数据,因为别的事务更改不了
ReadView并不能阻止事务1执行update或者delete语句来改动这个新插入的记录(由于事务2已经提交,因此改动该条记录并不会造成阻塞),但是这样一来,这条新记录的trx_id隐藏列的值就变成了事务1的事务id。之后事务1再使用普通的select语句去查询这条记录时就可以看到这条记录了.