MySQL相关(六)- 事务隔离级别的实现方案(MVCC)

前言

上一篇文章咱们介绍了MySQL 的四大事务特性 ACID,以及innodb 的事务隔离级别RU,RC,RR,可串行化,在结尾的时候我还卖了个关子,让你们思考一下 innodb 的事务隔离级别在 MySQL 中是如何实现的,不知道你们思考得咋样了,anyway,咱们今天就在这里继续讲关于事务隔离级别的实现方案。php

老规矩,先上飞机票:程序员

  1. MySQL相关(一)- 一条查询语句是如何执行的
  2. MySQL相关(二)- 一条更新语句是如何执行的
  3. MySQL相关(番外篇)- innodb 逻辑存储结构
  4. MySQL相关(三)- 索引数据模型推演及 B+Tree 的详细介绍
  5. MySQL相关(四)- 性能优化关键点索引
  6. MySQL相关(五)- 事务特性及隔离级别的详细介绍

前面提到的脑图以下,想要完整高清图片能够到微信个人公众号下【6曦轩】下回复 MySQL 脑图获取: 在这里插入图片描述面试

正文

正文中的絮絮不休

咱们你们先思考一下,若是要解决读一致性的问题,保证一个事务中先后两次读取数据结果一致,实现事务隔离,应该怎么作?咱们有哪一些方法呢?你的思路是什么样的呢?数据库

整体上来讲,咱们有两大类的方案。性能优化

LBCC

第一种,我既然要保证先后两次读取数据一致,那么我读取数据的时候,锁定我要操做的数据,不容许其余的事务修改就好了。这种方案咱们叫作基于锁的并发控制 Lock Based Concurrency Control(LBCC)。微信

若是仅仅是基于锁来实现事务隔离,一个事务读取的时候不容许其余时候修改,那就意味着不支持并发的读写操做,而咱们的大多数应用都是读多写少的,这样会极大地影响操做数据的效率。架构

MVCC

因此咱们还有另外一种解决方案,若是要让一个事务先后两次读取的数据保持一致,那么咱们能够在修改数据的时候给它创建一个备份或者叫快照,后面再来读取这个快照就好了。这种方案咱们叫作多版本的并发控制 Multi Version Concurrency Control (MVCC)。并发

MVCC 的核心思想是: 我能够查到在我这个事务开始以前已经存在的数据,即便它在后面被修改或者删除了。在我这个事务以后新增的数据,我是查不到的。mvc

  • 问题:这个快照何时建立?读取数据的时候,怎么保证能读取到这个快照而不是最新的数据?这个怎么实现呢?

InnoDB 为每行记录都实现了两个隐藏字段: <br />DB_TRX_ID,6 字节:插入或更新行的最后一个事务的事务 ID,事务编号是自动递增的(咱们把它理解为建立版本号,在数据新增或者修改成新数据的时候,记录当前事务 ID)。 <br />DB_ROLL_PTR,7 字节:回滚指针(咱们把它理解为删除版本号,数据被删除或记录为旧数据的时候,记录当前事务 ID)。 <br />咱们把这两个事务 ID 理解为版本号。 在这里插入图片描述性能

第一个事务,初始化数据(检查初始数据)

//Transaction 1

begin;

insert into mvcctest values(NULL,'Jerry') ;

insert into mvcctest values(NULL,'jack') ;

commit;

此时的数据,建立版本是当前事务 ID,删除版本为空:

id name 建立版本 删除版本
1 Jerry 1 undefined
2 jack 1 undefined

第二个事务,执行第 1 次查询,读取到两条原始数据,这个时候事务 ID 是 2:

// Transaction 2
begin;
select * from mvcctest ;	-- (1) 第一次查询

第三个事务,插入数据:

// Transaction 3
begin;
insert into mvcctest values(NULL,'tom') ;
commit;

此时的数据,多了一条 tom,它的建立版本号是当前事务编号,3:

id name 建立版本 删除版本
1 Jerry 1 undefined
2 jack 1 undefined
3 tom 3 undefined

第二个事务,执行第 2 次查询:

Transaction 2
select * from mvcctest ;	(2) 第二次查询

MVCC 的查找规则:只能查找建立时间小于等于当前事务 ID 的数据,和删除时间大于当前事务 ID 的行(或未删除)。

也就是不能查到在个人事务开始以后插入的数据,tom 的建立 ID 大于 2,因此仍是只能查到两条数据。

第四个事务,删除数据,删除了 id=2 jack 这条记录:

Transaction 4
begin;
delete from mvcctest where id=2;
commit;

此时的数据,jack 的删除版本被记录为当前事务 ID,4,其余数据不变:

id name 建立版本 删除版本
1 Jerry 1 undefined
2 jack 1 4
3 tom 3 undefined

在第二个事务中,执行第 3 次查询:

Transaction 2
select * from mvcctest ;	(3) 第三次查询

查找规则:只能查找建立时间小于等于当前事务 ID 的数据,和删除时间大于当前事务 ID 的行(或未删除)。

也就是,在我事务开始以后删除的数据,因此 jack 依然能够查出来。因此仍是这两条数据。

第五个事务,执行更新操做,这个事务事务 ID 是 5:

Transaction 4
begin;
update mvcctest set name ='Mic' where id=1;
commit;

此时的数据,更新数据的时候,旧数据的删除版本被记录为当前事务 ID 5(undo),产生了一条新数据,建立 ID 为当前事务 ID 5:

id name 建立版本 删除版本
1 Jerry 1 5
2 jack 1 4
3 tom 3 undefined
1 Mic 5 undefined

第二个事务,执行第 4 次查询:

// Transaction 2
select * from mvcctest ;	(4) 第四次查询

查找规则:只能查找建立时间小于等于当前事务 ID 的数据,和删除时间大于当前事务 ID 的行(或未删除)。

由于更新后的数据 Mic 建立版本大于 2,表明是在事务以后增长的,查不出来。

而旧数据 Jerry 的删除版本大于 2,表明是在事务以后删除的,能够查出来。

经过以上演示咱们能看到,经过版本号的控制,不管其余事务是插入、修改、删除,第一个事务查询到的数据都没有变化。

在 InnoDB 中,MVCC 是经过 Undo log 实现的。

Oracle、Postgres 等等其余数据库都有 MVCC 的实现。

须要注意,在 InnoDB 中,MVCC 和锁是协同使用的,这两种方案并非互斥的。第一大类解决方案是锁,锁又是怎么实现读一致性的呢?

关于锁的知识已经在马不停蹄准备了,且听下回分解~

By the way

有问题?能够给我留言或私聊 有收获?那就顺手点个赞呗~

固然,也能够到个人公众号下「6曦轩」,

回复“学习”,便可领取一份 【Java工程师进阶架构师的视频教程】~

回复“面试”,能够得到: 【本人呕心沥血整理的 Java 面试题】

回复“MySQL脑图”,能够得到 【MySQL 知识点梳理高清脑图】

因为我咧,科班出身的程序员,php,Android以及硬件方面都作过,不过最后仍是选择专一于作 Java,因此有啥问题能够到公众号提问讨论(技术情感倾诉均可以哈哈哈),看到的话会尽快回复,但愿能够跟你们共同窗习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不按期坚持推送输出,欢迎你们关注~~~

在这里插入图片描述

相关文章
相关标签/搜索