MySQL面试系列:MVCC是怎么实现的?(三)

前言

上篇文章SQL查询语句是如何执行的提到了日志文件:binlog、redo log 、undo log,那接下来我们接着详细聊聊undo log和MVCC是怎么实现的。

一、事务隔离级别

先了解一个概念:隔离级别

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
读已提交(read-committed)
可重复读(repeatable-read)
串行化(serializable)
  • 读未提交:能够随意读取其他事务未提交的数据和已提交的数据。形象解释:所有的数据在一个屋子里面,任何事务修改对于其他事务都是可见的。
  • 读已提交:不能读取其他事务未提交的数据,但是能读到其他事务已提交的数据。形象解释:一个屋子里有了一个小隔间,事务没提交的时候数据都在小隔间,一但事务提交了,就把数据放到大厅中,其他事务都可见了。
  • 可重复读(默认设置):其他事务提交了数据也不可见,每次查询结果都是一致的。形象解释:每个事务都在自己的小隔间操作,其他事务提交了数据,放到了大厅,自己也是不知道的。
  • 串行化:所有事务串行执行。

隔离级别
你是否也想问这样一个:MySQL的事务隔离级别是怎么实现的呢?这就是我们接下来要聊的重要话题。

什么是幻读
当同一查询在不同时间产生不同的行集时,在事务内就会发生 所谓的幻象问题。例如,如果事务A SELECT执行两次,但是第二次返回的行却不是第一次返回,则该行是“ phantom ”行。
怎么解决
To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking. (官方文档)
翻译:next-key lock 即行锁间隙锁解决了幻读问题,(index-row lock with gap lock = next-key lock)

二、Undo log

Undo log是InnoDB MVCC事务特性的重要组成部分,也是数据回滚依赖的对象。

主备工作
新建一个表:

mysql> create table user(id int primary key, name varchar, age int); 

插入一行数据:

mysql> insert into user values("1","张三",30);

在数据库中实际存储的时候它还会有3个隐藏的字段:DB_TRX_ID(事务ID ) 、DB_ROLL_PTR(回滚指针)、DB_ROW_ID(主键ID)

字段作用
DB_TRX_ID表示插入或更新该行的最后一个事务的事务标识符
DB_ROLL_PTR表示指向该行回滚段(rollback segment)的指针,InnoDB 便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在 undo 中都通过链表的形式组织。
DB_ROW_ID行标识(该行ID随着插入新行而单调增加),如果表没有主键且没有唯一索引,InnoDB 会自动生成一个隐藏主键。 【思考为什么要有这样一个字段

每条记录的头信息(record header)里都有一个专门的bit(deleted_flag)来表示当前记录是否已经被删除,即delete数据的时候会用deleted_flag来标记该数据已删除。(不用深究,记住有这么一个字段就行)

如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择主键作为聚集索引、如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引、如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引。

用一张图来看看插入一条数据之后的样子:
在这里插入图片描述
再有更新语句的时候,我们来看看undo log中的存储情况:
在这里插入图片描述

在每次修改聚集索引页上的记录时,变更之前的记录都会写到undo日志中。可以根据回滚段指针找到最近一次修改之前的undo记录,而每条Undo记录又能再次找到之前的变更。

回滚操作也比较简单,读取老版本记录,做逆向操作即可:
对于删除操作,清理标记删除标记;
对于更新操作,将数据回滚到最老版本;
对于插入操作,直接删除聚集索引和二级索引记录。

三、MVCC是什么?怎么实现的?

英文全称为Multi-Version Concurrency Control,翻译为中文即 多版本并发控制。
InnoDB有两个非常重要的模块来实现MVCC,一个是undo日志,用于记录数据的变化轨迹,用于数据回滚,另外一个是Read View,用于判断一个session对哪些数据可见,哪些不可见。

Read View:它用于控制数据的可见性
在InnoDB中,只有读查询才会去构建ReadView视图,对于类似DML这样的数据更改,无需判断可见性,而是单纯的发现事务锁冲突,直接堵塞操作。

ReadView包含几个重要的变量用于判断记录的可见范围
ReadView::id 创建该视图的事务ID;
ReadView::m_ids 创建ReadView时,活跃的读写事务ID数组,有序存储;(“活跃”指的就是启动了,但还未提交)
ReadView::m_low_limit_id 设置为当前最大事务ID;
ReadView::m_up_limit_id m_ids集合中的最小值,如果m_ids集合为空,表示当前没有活跃读写事务,则设置为当前最大事务ID。

可见性判断逻辑:

  • 如果记录的trx_id小于ReadView::m_up_limit_id,则说明该事务在创建ReadView时已经提交了,肯定可见;
  • 如果记录的trx_id大于等于ReadView::m_low_limit_id,则说明该事务是创建readview之后开启的,肯定不可见;
  • 当trx_id在m_up_limit_id和m_low_limit_id之间时,如果在ReadView::m_ids数组中,说明创建readview时该事务是活跃的,其做的变更对当前视图不可见,否则对该trx_id的变更可见。

通过上面的判断,该数据变更还是不可见时,就尝试通过undo去构建老版本记录,直到找到可见的记录,或者到达undo链表头都未找到。

如果我们查询得到的是一条二级索引记录逻辑还稍微有点区别,我觉得你能把上面的东西说出来就可以了,这点细节估计一面面试官自己都不知道,后面研究也不迟。

不同隔离级别下,可见性的判断有很大的不同

  1. READ-UNCOMMITTED 在该隔离级别下会读到未提交事务所产生的数据更改,这意味着可以读到脏数据,因为它根本不会去检查可见性或是查看老版本。

  2. READ-COMMITTED 在该隔离级别下,可以在SQL级别做到一致性读,当事务中的SQL执行完成时,ReadView被立刻释放了,在执行下一条SQL时再重建ReadView。这意味着如果两次查询之间有别的事务提交了,是可以读到不一致的数据的。

  3. REPEATABLE-READ 可重复读和READ-COMMITTED的不同之处在于,当第一次创建ReadView后(例如事务内执行的第一条SEELCT语句),这个视图就会一直维持到事务结束。也就是说,在事务执行期间的可见性判断不会发生变化,从而实现了事务内的可重复读。

  4. SERIALIZABLE 序列化的隔离是最高等级的隔离级别,当一个事务在对某个表做记录变更操作时,另外一个查询操作就会被该操作堵塞住。同样的,如果某个只读事务开启并查询了某些记录,那么另外一个session对这些记录的更改操作是被堵塞的。

看完这些我们就可以来说说InnoDB ACID

  • Atomicity (原子性)
    所谓原子性,就是一个事务要么全部完成变更,要么全部失败。(undo log)
  • Consistency (一致性)
    一致性指的是数据库需要总是保持一致的状态,即使实例崩溃了,也要能保证数据的一致性。
  • Isolation (隔离性)
    隔离性是指多个事务不可以对相同数据同时做修改,事务查看的数据要么就是修改之前的数据,要么就是修改之后的数据。(四种隔离级别)
  • Durability(持久性)
    当一个事务完成了,它所做的变更应该持久化到磁盘上,永不丢失。(redo log)

启动事务时begin/start transaction,执行第一条SQL语句才会创建一个视图 ReadView

总结

Innodb使用一种称做ReadView(视图)的对象来判断事务的可见性(也就是ACID中的隔离性)。 Innodb在执行一个SELECT时会创建一个视图对象。
又通过undo log的版本链去查询可见数据。
对于RR隔离级别,视图的生命周期到事务提交结束,对于RC隔离级别,则每条查询开始时重分配事务。

在 REPEATABLE-READ隔离级别还能START TRANSACTION WITH CONSISTENT SNAPSHOT 显式开启(可忽略)

本章所有的知识点是必须掌握的,并且自己需要理一下怎么简短的回答面试的问题。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值