目录
1.什么是mvcc?
2.问题引入
3. MVCC实现原理?
3.1 隐藏字段
3.2 undo log 日志
3.2.1 undo log版本链
3.3 readview
3.3.1 当前读
编辑
3.3.2 快照读
3.3.3 ReadView中4个核心字段
3.3.4 版本数据链访问的规则(了解)
4.面试题
前言:mysql中的Redolog日志保证了事务的持久性,Undolog保证了事务的原子性和一致性(回滚)
那事务的隔离性是怎么保证的呢?
答:
1. 锁:排他锁(如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁)
2. mvcc:多版本并发控制
这里主要讲一下mvcc
1.什么是mvcc?
多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突
2.问题引入
下面是一行数据,事务2、3、4都并发的对这行数据进行修改,而事务5要查询两次这行的最新数据,问查询到的是哪一次事务执行到的结果?
如果你看到这里不知道结果,请你继续往下看
3. MVCC实现原理?
MVCC的具体实现,主要依赖于数据库记录中的隐式字段、undo log日志、readView。
3.1 隐藏字段
3.2 undo log 日志
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。
而update、delete的时候,产生的undo log日志不仅在回滚时需要,mvcc版本访问也需要,不会立即被删除。
3.2.1 undo log版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
下面是事务
3.3 readview
ReadView(读视图)是快照读。是一个事务在执行 快照读(SELECT 等语句) 时生成的,它保存了4个关键字段(3.4.3 有介绍)
3.3.1 当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,
如:select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读。
3.3.2 快照读
简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
读已提交 RC (Read Committed:每次select,都生成一个快照读。
可重复读 RR (Repeatable Read)(MySQL 的默认隔离级别):开启事务后第一个select语句才是快照读的地方。
3.3.3 ReadView中4个核心字段
ReadView(读取视图) 是一个事务在执行 快照读(SELECT 等语句) 时生成的,它保存了以下关键字段:
还以下面的例子来解释:
事务五第一次查询id为30的记录,此时:
m_ids:当前活跃的事务ID集合是事务三、四、五。因为在事务五第一次查询之前事务二已经提交了(事务执行完毕)。
min_trx_id:最小活跃事务ID是事务三的id为3
max_trx_id:预分配事务ID,当前最大事务ID+1(因为事务ID是自增的),为事务5的id+1是6
creator_trx_id:ReadView创建者的事务ID ,即为是事务五的id 5
不同的隔离级别,生成ReadView的时机不同:
读已提交 RC READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
可重复读 RRREPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
3.3.4 版本数据链访问的规则(了解)
①. trx_id == creator_trx_id ? 可以访问该版本
②. trx_id < min_trx_id ? 可以访问该版本
③. trx_id > max_trx_id ? 不可以访问该版本
④. min_trx_id <= trx_id <= max_trx_id ? 如果trx_id不在m_ids中是可以访问该版本的
还以下面5个事务来举例子:
还以事务五第一次查询举例分析可以得到以下结论
上述 ①~④ 是 InnoDB 在使用 MVCC 机制进行快照读(Snapshot Read)时判断一个版本是否可见的规则,目的是为了实现事务隔离性(尤其是可重复读)。
核心依据是将数据行上的 trx_id
与当前事务的 ReadView
(读取视图)中的一些字段做比较,判断是否可以“看到”这个版本。
详细解释:
①.当前事务的id(未知),等于事务创建者的id(这里是事务五),所以当前事务是事务五,说明当前数据是由“我自己”这个事务修改的。MVCC 当然允许自己看到自己改过的数据(包括未提交的),以确保 读到的是自己操作后的最新快照。
②.当前事务的id(未知),小于最小活跃事务ID(这里是事务三),所以当前事务是事务二,而事务二已经提交,
-
说明这个事务在我生成 ReadView 之前就已经提交了。
-
因此是 对我“可见”的历史版本数据。
③.当前事务的id(未知),大于最大活跃事务ID+1(这里是事务五),所以当前事务是事务六,事务六在此时ReadView 生成时,事务六还没有创建
-
所以它对我来说是“未来”的数据。
-
根据事务隔离的原则,我不能看见这个数据版本。
④. min_trx_id <= trx_id <= max_trx_id,这个事务是在我生成 ReadView 时是活跃的(ID 在 min~max 之间),但它不在活跃事务列表 m_ids
中,说明它已经 提交完成了,因此我可以看见它。和②.表达的意思一样
不同的隔离级别,生成ReadView的时机不同:
读已提交 RC READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
可重复读 RRREPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
举例: