innoDB引擎幻读和不可重复读的区别

一、事务的隔离级别

  1. 读未提交-read uncommitted(产生脏读现象):A事务读取到B事务未提交的修改内容
  2. 读已提交-read committed(产生不可重复读现象):A事务在B事务提交前后读取到的内容不一致,包括B事务增/删/改造成的影响
  3. 可重复读-repeatable read(产生幻读现象):A事务在B事务提交前后读取到的内容不一致,针对B事务的增/删操作
  4. 串行-serializable:加锁

二、当前读和快照读

  快照读:读取的是事务开始前的快照版本。

    select ...

  当前读:读取的是当前最新的数据版本。

    select ... for update,select ... lock in share mode

    insert、update、delete

三、InnoDB实现可重复读

  1. 对于快照读,使用MVCC并发版本控制)解决不可重复读。实现细节如下:

  (1)每张表有隐藏字段:

  • DATA_TRX_ID:最新更新当前记录行的事务ID。只有事务提交后才会更新。
  • DATA_ROLL_PTR:一个指针,指向当前记录行的上一个版本。通过这个指针将这行记录的多个版本关联到一起,形成一个undo log版本链。
  • DB_ROW_ID:隐藏的自增ID。如果数据表没有主键,InnoDB会为该列创建聚簇索引。
  • DELETE BIT:标识当前记录是否被删除。

  (2)针对增删改查操作

    1. 增加:将DATA_TRX_ID设置为当前事务ID。

    2. 删除:将DATA_TRX_ID设置为当前事务ID,设置DELETE BIT为true。

    3. 更新(包含增加和删除):

      • 用排他锁锁住当前行。
      • 记录redo log:将更新之后的数据记录到redo log中,用于恢复。
      • 记录undo log:将更新之后的数据记录到undo log中,用于回滚。将DATA_TRX_ID设置为当前事务ID,将DATA_ROLL_PTR指向undo log中的当前数据行更新之前的数据行,同时将旧记录的DATA_TRX_ID设置为当前事务ID,并设置旧纪录的DELETE BIT为true。

    4. 查找:如果当前数据行未被删除(DELETE BIT标志为true),只查找DATA_TRX_ID在当前事务ID之前的记录,确保该条记录在当前事务之前已存在。如果当前数据行已被删除,只查找DATA_TRX_ID在当前事务ID之后的记录,确保该条记录在当前事务之后才删除

  综上分析,对于快照读,不会读到其他事务之后更新的记录,解决了不可重复读问题。

  ------------------------------------------------20210219更新记录------------------------------------------------

  参考了资料(2),修改了之前理解不对的地方。

  上文第4点介绍查找时,存在误区,并不是拿该记录的DATA_TRX_ID跟当前事务ID做比较。首先介绍read view。

  read view 读视图

  一种数据结构,主要是用来做可见性判断的。对于RR隔离级别,一个事务中,只有在第一次select时才会创建;对于RC隔离级别,一个事务中,每次select都会创建。

  • low_limit_id:目前出现过的最大的事务ID+1,既下一个要被分配出去的事务ID
  • up_limit_id:创建该read view时trx_ids列表中的最小事务ID,如果列表为空,up_limit_id=low_limit_id
  • trx_ids:read view创建时,其他未提交的活跃事务ID列表,不包含当前事务和已在内存中已提交的事务
  • creator_trx_id:创建该read view的事务ID,事务ID是递增的

  更正的地方在于,读取一条数据,其DATA_TRX_ID存在三种情况,分别来判断对于当前事务的可见性:

  1. 大于等于low_limit_id,这条数据在当前read view之后才创建,是不可见的,跳转到4
  2. 小于up_limit_id,这条数据在当前read view创建前已存在(或者是当前事务),是可以见的,直接返回该条记录
  3. 大于等于up_limit_id小于low_limit_id
    1. 在trx_ids中能找到该记录的DATA_TRX_ID,说明创建read view时,修改这条记录的事务未提交,是不可见的,跳转到4
    2. 在trx_ids中没有找到该记录的DATA_TRX_ID,说明创建read view时,修改这条记录的事务已提交(或者是当前事务),是可见的,直接返回该条记录
  4. 对于不可见的记录,将根据undo log向上寻找上一条记录,跳转到1判断重新进行判断

  

  2.对于当前读使用Next-Key Lock锁住操作行及其上下范围。

  所以InnoDB实现的可重复读隔离级别,不存在幻读现象。

参考资料:

(1) https://www.cnblogs.com/axing-articles/p/11415763.html

(2)https://blog.csdn.net/Waves___/article/details/105295060

人生就像蒲公英,看似自由,其实身不由己。
原文地址:https://www.cnblogs.com/walker993/p/14167148.html