mysql是如何解决幻读的

幻读:
幻读指的是一个事务在进行一次查询之后发现某个记录不存在,然后会根据这个结果进行下一步操作,此时如果另一个事务成功插入了该记录,那么对于第一个事务而言,其进行下一步操作(比如插入该记录)的时候很可能会报错。从事务使用的角度来看,在检查一条记录不存在之后,其进行插入应该完全没问题的,但是这里却抛出主键冲突的异常。
简单来说:事务A的两次读之间有其他事务写操作,比如事务A统计年龄>30,当A两次读数据之间其他事务新添加了记录,所以事务A第二次读取到的数据突然多了一个,仿佛出现了幻觉一般,这就是一种幻读。
串行化:

  • 事务在读操作时,先加表级别的共享锁,直到事务结束才释放;
  • 事务在写操作时,先加表级别的排它锁,直到事务结束才释放;

串行化锁定了整张表,幻读不存在的!!!
多版本并发MVCC:
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(systemversionnumber)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
MVCC只在REPEATABLEREAD和READCOMMITTED两个隔离级别下工作。
MVCC快照读需满足条件:

  1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
  2. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。

MVCC解决了基于快照读下的幻读,事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。并不会读到其他事务的写操作!!!但是MVCC无法解决当前读下的幻读。在mvcc下,快照读select是不能读取别的事务,但是update修改是当前读操作,update之后的读操作时可以读取别的事务已经提交的数据。
当前读:

select * from tb where ? lock in share mode;
select * from tb where ? for update;
  • forupdate:IX锁(意向排它锁),即在符合条件的rows上都加了排它锁
  • lockinsharemode:是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁
  • 排它锁:X锁、写锁,事务A对一个资源加了X锁后只有A本身能对该资源进行读和写操作,其他事务对该资源的读和写操作都将被阻塞,直到A释放锁为止
  • 共享锁:S锁、读锁,事务A锁定的数据其他事务可以共享读该资源,但不能写,直到事务A释放

InnoDB默认开启间隙锁,innodb_locks_unsafe_for_binlog参数表示是否禁用GapLock间隙锁,默认值0,启动间隙锁
测试时,设置参数为1禁用间隙锁,演示幻读场景

SHOW VARIABLES LIKE '%innodb_locks_unsafe_for_binlog%'

start transaction;
select * from tb where id>100 for update;
update tb set product_num=product_num-1 where id>100;

由于Mysql Server会针对update和delete操作里面的where条件查找满足条件的记录,(查找的不是快照)然后Innodb引擎会返回的满足条件的加锁记录,当其他事务进行Insert 操作后,进行一次当前读时,就会读到其他事务 Insert 记录,可以明显的发现其会导致幻读。当前读不会读取其他事务(写操作)未提交的数据,且当前读会阻塞,一直等到其它写操作事务提交,才能读取数据,这样避免脏数据的发生。

锁的机制:
Next-Key Lock是Gap Lock(间隙锁)和Record Lock(行锁)的结合版,都属于Innodb的锁机制
select * from tb where id>100 for update;

  • 主键索引 id 会给 id=100 的记录加上 record行锁
  • 索引 id 上会加上 gap 锁,锁住 id(100,+无穷大)这个范围

其他事务对 id>100 范围的记录读和写操作都将被阻塞;
插入 id=1000的记录时候会命中索引上加的锁会报出事务异常;
Next-Key Lock会确定一段范围,然后对这个范围加锁,保证A在where的条件下读到的数据是一致的,因为在where这个范围其他事务根本插不了也删不了数据,都被Next-Key Lock锁堵在一边阻塞掉了。

posted @ 2021-06-15 18:02  郭慕荣  阅读(567)  评论(0编辑  收藏  举报