Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql隔离性

mysql中的隔离性原理详解

作者:meihaoshy

这篇文章主要介绍了mysql隔离性的原理,多版本并发控制(MVCC)是一种用来解决 读-写冲突 的无锁并发控制,为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事务ID关联,读操作只读该事务开始前的数据库的快照,需要的朋友可以参考下

数据库并发的三种场景

在这三种场景中 读-读几乎没有任何问题 所以我们不需要并发控制

写-写并发只需要加锁控制即可

所以说我们今天重点讨论下读-写并发

MVCC

基本介绍

多版本并发控制( MVCC )是一种用来解决 读-写冲突 的无锁并发控制

为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事务ID关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题

在我们理解MVCC之前 我们需要知道三个前提知识

我们下面就分别先介绍下这三个隐藏字段

三个前提知识介绍

三个隐藏字段

假设我们现在创建并且插入了一条数据 代码和显示如下

mysql> create table if not exists student(
name varchar(11) not null,
age int not null
);
mysql> insert into student (name, age) values ('张三', 28);
Query OK, 1 row affected (0.05 sec)
mysql> select * from student;
+--------+-----+
| name | age |
+--------+-----+
| 张三 | 28 |
+--------+-----+
1 row in set (0.00 sec)

实际上在Linux隐藏字段的效果就是

在这里插入图片描述

对于上图做出一定解释

undo log日志

mySQL 将来是以服务进程的方式,在内存中运行。我们之前所讲的所有机制:索引,事务,隔离性,日志等,都是在内存中完成的,即在 MySQL 内部的相关缓冲区中,保存相关数据,完成各种判断操作。然后在合适的时候,将相关数据刷新到磁盘当中的。

所以,我们这里理解undo log,简单理解成,就是 MySQL 中的一段内存缓冲区,用来保存日志数据的就行。

read view 快照

关于快照读的知识下面模拟MVCC场景的时候会讲

Read View就是事务进行 快照读 操作的时候生产的 读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

Read View 在 MySQL 源码中,就是一个类,本质是用来进行可见性判断的。 即当我们某个事务执行快照读的时候,对该记录创建一个 Read View 读视图,把它比作条件,用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个版本的数据。

下面是 ReadView 结构,但为了减少同学们负担,我们简化一下

class ReadView {
	// 省略...
private:
	/** 高水位:大于等于这个ID的事务均不可见*/
	trx_id_t m_low_limit_id;
	/** 低水位:小于这个ID的事务均可见 */
	trx_id_t m_up_limit_id;
	/** 创建该 Read View 的事务ID*/
	trx_id_t m_creator_trx_id;
	/** 创建视图时的活跃事务id列表*/
	ids_t m_ids;
	/** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG,
	* 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/
	trx_id_t m_low_limit_no;
	/** 标记视图是否被关闭*/
	bool m_closed;
	// 省略...
};
m_ids; //一张列表,用来维护Read View生成时刻,系统正活跃的事务ID
up_limit_id; //记录m_ids列表中事务ID最小的ID
low_limit_id; //ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1
creator_trx_id //创建该ReadView的事务ID

我们在实际读取数据版本链的时候,是能读取到每一个版本对应的事务ID的,即:当前记录的DB_TRX_ID 。

那么,我们现在手里面有的东西就有,当前快照读的 ReadView 和 版本链中的某一个记录的DB_TRX_ID 。

那么 现在我们的问题就是 当前快照读,应不应该读到当前版本记录。一张图,解决所有问题!

在这里插入图片描述

如果查到不应该看到当前版本,接下来就是遍历下一个版本,直到符合条件,即可以看到。上面的readview 是当你进行select的时候,会自动形成。

看到这里有的同学可能会产生这样一个疑问 如何遍历下个版本呢?

我们之前说过 undo log其实就是一个缓冲区 并且里面有着回滚指针连接着的各种数据 (实际上就是单链表连接的各种数据)

再次总结下

转化成现实中的例子

现在的我们能够看到我们出生之前所有人写的作品 但是我们不能看到还未出生的人写的作品

如果说写书的人跟我们同一个时代 我们就要判断这本书有没有发表 (是否提交) 如果提交了我们就能看见 如果没有提交 我们就不能看见

模拟MVCC场景

MVCC场景中有增删改查 下面我们分别进行讨论

我们插入的时候只需要形成一条新的undo log版本链 将回滚指针指向前面的数据 如果需要回滚直接通过回滚指针找到需要覆盖的数据进行覆盖即可

我们前面说过了 mysql中还有一个隐藏的falg字段 因此 如果需要删除的话 只需要将flag标志位设置即可

这是最麻烦的一个环节 我们使用一个例子来说明MVCC中的改

现在一个表中有如下的记录

在这里插入图片描述

现在有一个事务ID为10的事务 要修改表中的name张三为李四

过程图如下

在这里插入图片描述

如果还有事务要修改新的数据就参考上面的步骤即可

于是乎我们就形成了一条基于链表记录的历史版本链 undo log里面的一个个历史版本就称为快照

现在我们明白了

首先我们要理解两个概念 当前读和快照读

多个事务同时增删改的时候是当前读 需要加锁 在串行化的隔离级别下 select也是当前读 需要加锁

如果select是快照读 那么和增删改的当前读不冲突 所以说并行效率高 事务的隔离级别决定了select是当期读还是快照读 具体的判断方法可以参考前面read view部分的知识

RR和RC的区别

RR级别测试

演示一 两边开启事务 右边先进行快照读 左边插入数据之后commit 右边再进行快照读和当前读

在这里插入图片描述

在这里插入图片描述

我们可以发现的是 当右边使用快照读的时候不管左边有没有commit 读取到的数据是一样的

而使用当前读的时候 我们可以发现读取的数据就是最新的数据了 光靠这个一个试验我们看不出来什么 接下来我们看演示二

演示二: 左右两边同时开启一个事务 左边先插入数据之后提交 右边在左边提交之后进行快照读

在这里插入图片描述

我们发现 这个时候右边的快照读 读取了最新的数据

对比这两次试验加上之前的read view部分学习我们不难做出以下的推断

在RR级别下 第一次select快照读的时候会生成一个read view快照 之后的读取就按照这个快照进行

而实际上在RC级别中 每一次的select快照都都会生成一个最新的read view快照

所以说RR和RC最本质的区别就是 RR只会生成依次read view快照 而RC快照读几次就会生成几次快照

四种隔离级别的不同处理方式

读–未提交

直接当前读 不加锁

串行化

当前读 加锁

读 提交

在RC级别中 每次的select读取都是快照读 每次都会生成一个最新的read view快照

可重复读

在RR级别中 每次select读取都是快照读 并且都会遵循第一次select读取时生成的read view快照

总结

在这里插入图片描述

到此这篇关于mysql隔离性的原理的文章就介绍到这了,更多相关mysql隔离性内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文