硬核解析MySQL的MVCC实现原理,面试官看了都直呼内行
1. 什么是MVCC
MVCC全称是Multi-Version Concurrency Control(多版本并发控制),是一种并发控制的方法,通过维护一个数据的多个版本,减少读写操作的冲突。
如果没有MVCC,想要实现同一条数据的并发读写,还要保证数据的安全性,就需要操作数据的时候加读锁和写锁,这样就降低了数据库的并发性能。
有了MVCC,就相当于把同一份数据生成了多个版本,在操作的开始各生成一个快照,读写操作互不影响。无需加锁,也实现数据的安全性和事务的隔离性。
事务的四大特性中隔离性就是基于MVCC实现的。
说MVCC的实现原理之前,先说一下事务的隔离级别。
2. 事务的隔离级别
说隔离级别之前,先说一下并发事务产生的问题:
脏读: 一个事务读到其他事务未提交的数据。
不可重复读: 相同的查询条件,多次查询到的结果不一致,即读到其他事务提交后的数据。
幻读: 相同的查询条件,多次查询到的结果不一致,即读到其他事务提交后的数据。
不可重复读与幻读的区别是: 不可重复读是读到了其他事务执行update、delete后的数据,而幻读是读到其他事务执行insert后的数据。
再说一下事务的四大隔离级别:
Read UnCommitted(读未提交): 读到其他事务未提交的数据,会出现脏读、不可重复读、幻读。
Read Committed(读已提交): 读到其他事务已提交的数据,解决了脏读,会出现不可重复读、幻读。
Repeatable Read(可重复读): 相同的条件,多次读取到的结果一致。解决了脏读、不可重复读,会出现幻读。
Serializable(串行化): 所有事务串行执行,解决了脏读、不可重复读、幻读。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 会 | 会 | 会 |
读已提交 | 不会 | 会 | 会 |
可重复读 | 不会 | 不会 | 会 |
串行化 | 不会 | 不会 | 不会 |
MVCC只在Read Committed和Repeatable Read两个隔离级别下起作用,因为Read UnCommitted隔离级别下,读写都不加锁,Serializable隔离级别下,读写都加锁,也就不需要MVCC了。
再谈一下Undo log日志。
3. Undo Log(回滚日志)
Undo Log记录的是逻辑日志,也就是SQL语句。
比如:当我们执行一条insert语句时,Undo Log就记录一条相反的delete语句。
作用:
回滚事务时,恢复到修改前的数据。
实现 MVCC 。
事务四大特性中原子性也是基于Undo Log实现的。
下面开始谈一下MVCC的实现原理。
4. MVCC的实现原理
4.1 当前读和快照读
先普及一下什么是当前读和快照读。
当前读: 读取数据的最新版本,并对数据进行加锁。
例如:insert、update、delete、select for update、 select lock in share mode。
快照读: 读取数据的历史版本,不对数据加锁。
例如:select
MVCC是基于Undo Log、隐藏字段、Read View(读视图)实现的。
4.2 隐藏字段
先说一下MySQL的隐藏字段,当我们创建一张表时,InnoDB引擎会增加2个隐藏字段。
DB_TRX_ID(最近一次提交事务的ID):修改表数据时,都会提交事务,每个事务都有一个唯一的ID,这个字段就记录了最近一次提交事务的ID。
DB_ROLL_PTR(上个版本的地址):修改表数据时,旧版本的数据都会被记录到Undo Log日志中,每个版本的数据都有一个版本地址,这个字段记录的就是上个版本的地址。
4.3 版本链
当我们第一次往用户表插入一条记录时,表数据和隐藏字段的值是下面这样的:
insert into user (name,age) values ('一灯',1);
事务ID(DB_TRX_ID)是1,上个版本地址(DB_ROLL_PTR)是null。
第二次提交事务,把用户年龄加1。
update user set age=age+1 where id=1;
事务ID变成2,上个版本地址指向Undo Log中的记录。
第三次提交事务,再把用户年龄加1。
update user set age=age+1 where id=1;
事务ID变成3,上个版本地址指向Undo Log中事务ID为2的记录。
这样表记录和Undo Log历史数据就组成了一个版本链。
4.4 Read View(读视图)
在事务中,执行SQL查询,就会生成一个读视图,是用来保证数据的可见性,即读到Undo Log中哪个版本的数据。
快照读一般是读取的历史版本的读视图,当前图会生成一个最新版本的读视图。
读视图是基于下面几个字段实现的:
m_ids :当前系统中活跃的事务ID集合,即未提交的事务。
min_trx_id :m_ids中最小的ID
max_trx_id :下一个要分配的事务ID
creator_trx_id: 当前事务ID
读视图决定当前事务能读到哪个版本的数据,从表记录到Undo Log历史数据的版本链,依次匹配,满足哪个版本的匹配规则,就能读到哪个版本的数据,一旦匹配成功就不再往下匹配。
数据可见性规则:
- DB_TRX_ID = creator_trx_id
如果这个版本数据的事务ID等于当前事务ID,表示数据记录的最后一次操作的事务就是当前事务,当前读视图可以读到这个版本的数据。 - DB_TRX_ID < min_trx_id
如果这个版本数据的事务ID小于所有活跃事务ID,表示这个版本的数据不再被事务使用,即事务已提交,当前读视图可以读到这个版本的数据。 - DB_TRX_ID >= max_trx_id
如果这个版本数据的事务ID大于等于下一个要分配的事务ID,表示有新事务更新了这个版本的数据,这种情况下,当前读视图不可以读到这个版本的数据。 - min_trx_id <= DB_TRX_ID < max_trx_id
如果这个版本数据的事务ID在当前系统中活跃的事务ID集合(m_ids)里面,表示这个版本的数据被其他事务更新过,当前读视图不可以读到这个版本的数据。
如果这个版本数据的事务ID不在当前系统中活跃的事务ID集合(m_ids)里面,表示是在其他事务提交后创建的读视图,当前读视图可以读到这个版本的数据。
5. 不同隔离级别下可见性分析
在不同的事务隔离级别下,生成读视图的规则不同:
- READ COMMITTED(读已提交) :在事务中每一次执行快照读时都生成一个读视图,每个读视图中四个字段的值都是不同的。
- REPEATABLE READ(可重复读):仅在事务中第一次执行快照读时生成读视图,后续复用这个读视图。
5.1 READ COMMITTED(读已提交)
设置MySQL隔离级别为读已提交:
SET session TRANSACTION ISOLATION LEVEL READ COMMITTED;
执行两个事务,验证一下:
事务1第一次查询时,会生成一个读视图,读视图的各个属性如下:
属性 | 值 |
---|---|
m_ids | 1,2 |
min_limit_id | 1 |
max_limit_id | 3 |
creator_trx_id | 1 |
可见的版本链数据是:
符号规则 DB_TRX_ID = creator_trx_id = 1
,可以看到当前版本的数据。
事务1第二次查询时,会生成一个新的读视图,读视图的各个属性如下:
属性 | 值 |
---|---|
m_ids | 1 |
min_limit_id | 1 |
max_limit_id | 3 |
creator_trx_id | 1 |
可见的版本链数据是:
符号规则 min_trx_id <= DB_TRX_ID < max_trx_id(1<=2<3)
,并且当前数据版本的事务ID不在当前系统中活跃的事务ID集合,可以看到当前版本的数据。
同一个事务内,相同的查询条件,查询到的数据不一致,查到了其他事务更新过的数据,也就是出现了不可重复读的情况。
再看一下,在可重复读隔离级别下,是怎么解决这个问题的。
5.2 REPEATABLE READ(可重复读)
设置MySQL隔离级别为可重复读:
SET session TRANSACTION ISOLATION LEVEL REPEATABLE READ;
执行两个事务,验证一下:
事务1第一次查询时,会生成一个读视图,读视图的各个属性如下:
属性 | 值 |
---|---|
m_ids | 1,2 |
min_limit_id | 1 |
max_limit_id | 3 |
creator_trx_id | 1 |
可见的版本链数据是:
符号规则 DB_TRX_ID = creator_trx_id = 1
,可以看到当前版本的数据。
事务1第二次查询时,会复用原有的读视图,读视图的各个属性如下:
属性 | 值 |
---|---|
m_ids | 1,2 |
min_limit_id | 1 |
max_limit_id | 3 |
creator_trx_id | 1 |
可见的版本链数据是:
符号规则 min_trx_id <= DB_TRX_ID < max_trx_id(1<=2<3)
,并且当前数据版本的事务ID在当前系统中活跃的事务ID集合,所以是不可以看到当前版本的数据。
由此得知,可重复读隔离级别下,相同的查询条件,两次查询到的结果相同,也就是解决了可重复读的问题,是通过复用原有的读视图的方式解决的。
硬核解析MySQL的MVCC实现原理,面试官看了都直呼内行的更多相关文章
- 深入解析Mysql 主从同步延迟原理及解决方案
MySQL的主从同步是一个很成熟的架构,优点为:①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力;②在从主服务器进行备份,避免备份期间影响主服务器服务;③当主服务器出现问题时,可以 ...
- 「MySQL高级篇」MySQL之MVCC实现原理&&事务隔离级别的实现
大家好,我是melo,一名大三后台练习生,死去的MVCC突然开始拷打我! 引言 MVCC,非常顺口的一个词,翻译起来却不是特别顺口:多版本并发控制. 其中多版本是指什么呢?一条记录的多个版本. 并发控 ...
- 云栖干货回顾 | 云原生数据库POLARDB专场“硬核”解析
POLARDB是阿里巴巴自主研发的云原生关系型数据库,目前兼容三种数据库引擎:MySQL.PostgreSQL.Oracle.POLARDB的计算能力最高可扩展至1000核以上,存储容量可达100TB ...
- 掌握这四大MySQL知识点,吊打面试官
作为一名后端开发,MySQL的使用必不可少,合理的使用索引和索引调优是后端开发者必须掌握的技能之一. 在日常数据库的问题当中,不合理的使用索引占大部分. MySQL是大家工作上最常用的关系型数据库之一 ...
- 【MySQL】我这样分析MySQL中的事务,面试官对我刮目相看!!
写在前面 相信大部分小伙伴在面试过程中,只会针对面试官提出的表面问题来进行回答.其实不然,面试官问的每一个问题都是经过深思熟虑的,面试的时间相对来说也是短暂的,面试官不可能在很短的时间内就对你非常了解 ...
- 20170103简单解析MySQL查询优化器工作原理
转自博客http://www.cnblogs.com/hellohell/p/5718238.html 感谢楼主的贡献 查询优化器的任务是发现执行SQL查询的最佳方案.大多数查询优化器,包括MySQL ...
- 超硬核解析!Apache Hudi灵活的Payload机制
Apache Hudi 的Payload是一种可扩展的数据处理机制,通过不同的Payload我们可以实现复杂场景的定制化数据写入方式,大大增加了数据处理的灵活性.Hudi Payload在写入和读取H ...
- 硬核剖析ThreadLocal源码,面试官看了直呼内行
工作面试中经常遇到ThreadLocal,但是很多同学并不了解ThreadLocal实现原理,到底为什么会发生内存泄漏也是一知半解?今天一灯带你深入剖析ThreadLocal源码,总结ThreadLo ...
- 阿里P7面试官:请你简单说一下类加载机制的实现原理?
面试题:类加载机制的原理 面试官考察点 考察目标: 了解面试者对JVM的理解,属于面试八股文系列. 考察范围: 工作3年以上. 技术背景知识 在回答这个问题之前,我们需要先了解一下什么是类加载机制? ...
随机推荐
- 安装pystaller
安装命令 # -i指定下载地址,此处采用清华大学镜像 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package pyin ...
- API概述,使用步骤和Scanner概述及其API文档的使用
API概述 API(Application Programming Interface),应用程序编程接口.Java API是一本程序员的 字典 ,是JDK中提供给 我们使用的类的说明文档.这些类将底 ...
- STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- 从零开始手写Cartographer(1): 开端
写在前面的话 我做SLAM已经三年了.读书时初学SLAM,一开始无从下手,直到读了高博士的博客,茅塞顿开,渐入佳境.后来又买了他的<视觉SLAM十四讲>,常伴手边,直至毕业.几个月前找工作 ...
- 一篇文章带你走进meta viewport的世界
一.什么是 meta 标签? 可提供有关页面的元信息 二.为什么需要移动端适配? 因为我们在 pc 端上看到的页面都是比较大的,在 pc 端上都是正常显示的,自动不会被进行缩放,除非手动进行放大或缩小 ...
- 2022-7-23 pan小堂 Object与Final
Object类 1.Object方法 public final native Class<?> getClass() 返回object运行时类 public native int hash ...
- Javascript 函数声明、调用、闭包
1 # Javascript 函数声明.调用.闭包 2 # 一.函数声明 3 # 1.直接声明.浏览器在执行前,会先将变量和函数声明进行提升. 4 fn(); 5 function fn () { 6 ...
- 妙用 CSS 构建花式透视背景效果
本文将介绍一种巧用 background 配合 backdrop- filter 来构建有趣的透视背景效果的方式. 本技巧源自于一名群友的提问,如何构建如 ElementUI 文档的一种顶栏背景特效, ...
- 一键到位「GitHub 热点速览 v.22.32」
作者:HelloGitHub-小鱼干 上上周在 B 站观看了智能键盘--瀚文的制作过程,本周 GitHub 热榜上出现了它的软硬件开源项目 HelloWord-Keyboard,如果你的动手能力强不妨 ...
- 业务可视化-让你的流程图"Run"起来(6.定时任务&Spring-Batch的集成)
前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(5.SpringBoot集成&微服务编排)]的支持. 分享一下近期我对这个项目的一些改进. 在项目 ...