看了网上一些MVCC的讲解,总结以下几个误区。
1、MVCC原理——没有创建版本号和删除版本号的概念
简化版(通过创建时间版本号
和过期(删除)时间版本号
):
-
每行数据实际上隐藏了两列,创建时间版本号,过期(删除)时间版本号,每开始一个新的事务,版本号都会自动递增。
-
MVCC的原理是查找创建版本小于或等于当前事务版本,删除版本为空或者大于当前事务版本。
真正原理:
-
事实上,上述的说法只是简化版的理解,真正的MVCC用于读已提交和可重复读级别的控制,主要通过undo log日志版本链和read view来实现。
-
每条数据隐藏的两个字段也并不是
创建时间版本号
和过期(删除)时间版本号
,而是roll_pointer
和trx_id
。 -
roll_pointer指向更新事务之前生成的undo log,undo log用于事务的回滚,保证事务的原子性。
-
trx_list:一个数值列表,用来维护Read View生成时刻系统正活跃的事务ID
up_limit_id:记录trx_list列表中事务ID最小的ID
low_limit_id:Read View生成时刻系统尚未分配的下一个事务ID
每次快照读的时候都会拿当时事务creator_trx_id和up_limit_id以及low_limit_id进行比较(trx_id一定小于low_limit_id),RC和RR的区别是,RC每次快照读生成read view,RR只在事务开始的时候生成一个
。
-
trx_id<up_limit_id:说明是快照读之前事务,可以直接返回
-
up_limit_id<trx_id<low_limit_id:
-
trx_id = creator_trx_id:说明是自己改的直接返回
-
trx_id并且在 trx_list中,说明是我快照的时候还未提交的数据,要通过roll_pointer去undolog中找到之前的数据
2、up_limit_id和low_limit_id
-
up_limit_id:记录trx_list列表中事务ID最小的ID
-
low_limit_id:Read View生成时刻系统尚未分配的下一个事务ID
3、RR级别下,InnoDB是否解决了幻读
-
对于快照读,RR中一个事务的所有快照读读取的都是同一份快照,所以无论其他的事务怎么修改,无论是更新还是插入删除,都不会影响当前事务的快照读结果,也就不会出现不可重复读、幻读的情形。
-
对于当前读,你读取的行,以及行的间隙都会被加锁,直到事务提交时才会释放,其他的事务无法进行修改,所以也不会出现不可重复读、幻读的情形。
-
所以如果你总是进行快照读,或者总是进行当前读,是不会出现幻读的情况的。
-
只有一种情况会出现幻读:先进行了一次快照读(select),读取了历史数据,再进行了一次当前读(insert row with id = 1),读取最新数据,这样当然会出现幻读了。
4、间隙锁
- 查询条件不走索引,锁住所有间隙,等价于表锁
- 查询条件走普通索引,锁住查询条件附近的间隙
- 查询条件走唯一索引,只锁一条数据,退化成行锁
5、解释下MVCC
-
MVCC解释为多版本并发控制,是一种并发控制的理念,维持一个数据多个版本,使得读写操作没有冲突。
-
MVCC在InnoDB中目的是提高数据库并发性能,在有读写冲突的情况下,也能做到不加锁,非阻塞读。(传统解决读写传统一般会使用悲观锁)
-
快照读就是MVCC在MYSQL中非阻塞读功能的实现。
-
MVCC的实现原理是依靠4个隐式字段+undo log+read view来实现的。4个隐式字段包括trx_id最近修改的事务ID,roll_pointer回滚指针主要是这两个,还有两个是隐式主键和删除flag
-
然后read view里有4个关键的字段trx_list,up_limit_id,low_limit_id,creator_trx_id,在rc情况下每次快照读都会生成一个read view ,在rr隔离级别下的话是每次事务开启的时候会生成一个read view。
-
然后关键的就是回使用查到的数据中的trx_id 和up_limit_id、low_limit_id、creator_trx_id去进行比较这里有小于、等于、在范围里三种情况,如果trx_id在范围里且在trx_list中的话,就提高roll_pointer去undo log找到之前版本的数据再返回。