innoDB引擎幻读和不可重复读的区别
一、事务的隔离级别
- 读未提交-read uncommitted(产生脏读现象):A事务读取到B事务未提交的修改内容
- 读已提交-read committed(产生不可重复读现象):A事务在B事务提交前后读取到的内容不一致,包括B事务增/删/改造成的影响
- 可重复读-repeatable read(产生幻读现象):A事务在B事务提交前后读取到的内容不一致,针对B事务的增/删操作
- 串行-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存在三种情况,分别来判断对于当前事务的可见性:
- 大于等于low_limit_id,这条数据在当前read view之后才创建,是不可见的,跳转到4
- 小于up_limit_id,这条数据在当前read view创建前已存在(或者是当前事务),是可以见的,直接返回该条记录
- 大于等于up_limit_id小于low_limit_id
- 在trx_ids中能找到该记录的DATA_TRX_ID,说明创建read view时,修改这条记录的事务未提交,是不可见的,跳转到4
- 在trx_ids中没有找到该记录的DATA_TRX_ID,说明创建read view时,修改这条记录的事务已提交(或者是当前事务),是可见的,直接返回该条记录
- 对于不可见的记录,将根据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