简述MySQL中的MVCC

MySQL是我们在开发中都要用到的关系型数据库.MySQL事务遵循ACID四要素

原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

.随着互联网的发展,MySQL也会发生高并发的并发访问,这就存在多线程访问之间的数据不一致问题.
针对这个问题,MySQL采取 锁和MVCC(多版本并发控制)的方式来确保数据一致性问题.

MySQL有四种隔离级别

分别为:

隔离级别脏读幻读不可重复读
读已提交
----
不可重复读
----
可重复读
----
串行化

MySQL默认的事务隔离级别是: 可重复读.

MySQL的锁:

因为MySQL 默认的存储引擎是 InnerDB,以下都 以InnerDB作为 基础.
MySQL有读锁和写锁两种方式.读锁是共享锁,写锁是独占锁.
innerDB 是采用行锁的.
当我们开启事务之后, 在读取数据的时候, 是会给要读取的这一行加一个读锁,此时如果有其他线程要读取这一行的数据,是没有任何影响的,因为读锁是对读操作共享的,但是如果此时有其他线程要对这一列进行写操作的话,那么这个写操作的线程就会阻塞在这个地方,知道读取这一列的线程释放读锁.写线程才可以执行写操作.
在进行写操作的时候,我们也会给这一行数据加一个写锁.此时,如果有其他线程要对这一行数据进行操作,无论是读操作还是写操作,那么都会阻塞在这里,知道写线程释放掉读锁.

这里看起来很完美,保证了我们的数据一致性.但是 加锁毕竟是一个损耗性能的事情,而且线程阻塞也是会降低我们的效率.如果并发特别高的话,还可能会造成阻塞时间过长,

所以为了保证性能,Mysql做出了一定的妥协,就是采用了MVCC机制.

MVCC机制

MVCC机制可以看做是MySQL对行锁的一种优化或者说是妥协,它在许多情况下避免了使用锁,同时可以提供更小的开销。根据实现的不同,它可以允许非阻塞式读,在写操作进行时只锁定必要的记录。
当有事务进来操作数据库的时候,就会形成一个事务id,事务id是不重复且不断增加的. 这也是形成事务版本链的一个基本组成原理.,

首先看一下这个图.
在这里插入图片描述
此时 有A,B,C三个写操作过来要操作数据库A 表中的一行数据…而D过来要读取这一行数据. 而且ABC均开启了事务.

在这里插入图片描述
我做了一个Excel 来表示 SQL语句执行顺序 begin 说明开启事务,然后 用从上到下的顺序 来表示 执行顺序

当seesion D 第一次执行 查询 语句的时候, 这个时候会生成一个 一致性的视图 read-view… 这里的一致性是说的,在一个查询的session里面,这个视图会一直存在.

这个视图是由此时 这个表里面所有 在操作的未提交事务和已提交的事务共同组成的一个 快照.
其中维护一个数组, 这个数组是由 所有未提交的 事务的id 组成,还有一个已经创建的最大id事务的id.
如我们模拟的操作 第一次执行的时候 形成的 read-view 就是[100,200] 300
为了方便后面理解,我们把数组中最小的id 称为minId(100), 已创建的最大事务id 称为 maxId(300)
当我们进行第一次查询的时候,此时我们遍历版本链,
如图
在这里插入图片描述

我们每执行一条语句,MySQL都会将这个语句存放到Undo日志里面,方便我们回滚.同时保证 每一个语句后面都有一个指针指向之前的一个语句.

上面形成的就是版本链.
在比对版本链的时候 有三条规则:
1.如果事务id是大于maxId,那么表示这个事务是还没有发生的事务,所以肯定是不可见的.
2.如果事务id是小于minId,那么表示这个事务是已经提交的事务生成的,这个数据是可见的,那么久可以访问到对应版本的数据
3.如果事务id大于等于minId,小于等于maxId,那么表示这个事务有两种情况,
3.1 事务id在 还未提交的事务id数组里面,表示 这个事务还没有提交,是不可见的,但是如果是当前事务自己查询自己事务内的操作的话,那么是课件的.
3.2 事务id 不在未提交的事务id数组里面,表示 这个事务已经提交了,是可见的.

遵循这3条规则,我们查询第一条语句的时候,就会发现, sessionD 读取的时候 因为没有开启事务,所以直接就能查到离他最近的提交事务,所以第一条语句的结果是 age = 30;
而此时就已经形成了一致性视图 , 所以 无论后面 session A 和session B 是如何去修改 id=1 的age 都是会按照这个视图去查看,所以结果一直会是age = 30;

直接拿最后一次查询来做一下说明:
在这里插入图片描述
此时版本链的最开始是 事务id=200 , 而我们此时查询 依然会遵循我们一开始创建的视图 也就是 read_view[100,200] 300
200是大于200小于300 的, 而且他是在 未提交事务的数组里面的,所以,我们会pass掉这一条而进入一条指令.
而根据这个逻辑,最后这些100,200的事务都是不满足的,所以最后还是找到了事务id=300,也就是age=30;

下面是MySQL对应的DML命令 版本号的一些操作:
INSERT:InnoDB为这个新行记录当前的系统版本号。
DELETE:InnoDB将当前的系统版本号设置为这一行的删除ID。
UPDATE:InnoDB会写一个这行数据的新拷贝,这个拷贝的版本为当前的系统版本号。它同时也会将这个版本号写到旧行的删除版本里。

这种额外的记录所带来的结果就是对于大多数查询来说根本就不需要获得一个锁。他们只是简单地以最快的速度来读取数据,确保只选择符合条件的行。这个方案的缺点在于存储引擎必须为每一行存储更多的数据,做更多的检查工作,处理更多的善后操作。

而删除操作可以认为为特殊的update,会将版本脸上最新的数据复制一份,然后将事务id修改成删除操作的事务id,同时在该记录的头信息上将deleted flag 标记为true,表示该条记录已被删除,在查询时按照上面规则查到的对应的记录如果是 对应的deleted flag 是true的话,以为着记录已经被删除,则不会返回数据.

值得注意的是:

MVCC只工作在可重复读和读已提交隔离级别下。MVCC是不兼容不可重复读,因为查询不能找到适合他们事务版本的行版本;它们每次都只能读到最新的版本。串行化也不与MVCC兼容,因为读操作会锁定他们返回的每一行数据 .

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值