mysql数据库 | MVCC
发布日期:2022-02-21 17:40:22 浏览次数:29 分类:技术文章

本文共 2480 字,大约阅读时间需要 8 分钟。

mysql数据库 | MVCC

MVCC (Multi-Version Concurrency Control) 多版本并发控制协议—MVCC

MySQL InnoDB存储引擎,实现的是基于mvcc的

MVCC最大的好处:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。

1. MVCC应用场景

MySQL 中 InnoDB 引擎支持 MVCC;

应对高并发事务, MVCC 比单纯的加行锁更有效, 开销更小;

MVCC 只在读已提交(Read Committed)可重复读(Repeatable Read)隔离级别下起作用,其他两个隔离级别和MVCC不兼容;(对于 Serializable 隔离级别,是通过加锁互斥来访问数据,因此不需要 MVCC )

MVCC 既可以基于乐观锁又可以基于悲观锁来实现。

2. MVCC可以解决什么问题

  • 读写之间阻塞的问题:通过 MVCC 可以让读写互相不阻塞,读不相互阻塞,写不阻塞读,这样可以提升数据并发处理能力。
  • 降低了死锁的概率:这个是因为 MVCC 采用了乐观锁的方式,读取数据时,不需要加锁,写操作,只需要锁定必要的行。
  • 解决了一致性读的问题:当我们朝向某个数据库在时间点的快照是,只能看到这个时间点之前事务提交更新的结果,不能看到时间点之后事务提交的更新结果。

3. 快照读和当前读

3.1 快照读

快照读,读取的是快照数据,不加锁的简单 Select 都属于快照读.

SELECT * FROM player WHERE ...

3.2 当前读

当前读就是读的是最新数据,而不是历史的数据,加锁的 SELECT,或者对数据进行增删改都会进行当前读。

SELECT * FROM player LOCK IN SHARE MODE;SELECT FROM player FOR UPDATE;INSERT INTO player values ...DELETE FROM player WHERE ...UPDATE player SET ...

4. MVCC原理

实际上MVCC的原理就是Read View 和 undo log

4.1 事务版本号

每开启一个日志,都会从数据库中获得一个事务ID(也称为事务版本号),这个事务 ID 是自增的,通过 ID 大小,可以判断事务的时间顺序。

4.2 行记录的隐藏列

  1. row_id :隐藏的行 ID ,用来生成默认的聚集索引。如果创建数据表时没指定聚集索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚集索引的方式可以提升数据的查找效率。
  2. trx_id: 操作这个数据事务 ID ,也就是最后一个对数据插入或者更新的事务 ID 。
  3. roll_ptr:回滚指针,指向这个记录的 Undo Log 信息。
    在这里插入图片描述

4.3 Undo Log

InnoDB 将行记录快照保存在 Undo Log 里。

在这里插入图片描述
数据行通过快照记录都通过链表的结构的串联了起来,每个快照都保存了 trx_id 事务ID,如果要找到历史快照,就可以通过遍历回滚指针的方式进行查找。

4.4 Read View

如果一个事务要查询行记录,需要读取哪个版本的行记录呢? Read View 就是来解决这个问题的。Read View 可以帮助我们解决可见性问题。 Read View 保存了当前事务开启时所有活跃的事务列表。换个角度,可以理解为: Read View 保存了不应该让这个事务看到的其他事务 ID 列表。

说人话:所有正在进行的事务都在Read View中

字段:

  • trx_ids :系统当前正在活跃的事务ID集合。
  • low_limit_id :活跃事务的最大的事务 ID。
  • up_limit_id :活跃的事务中最小的事务 ID。
  • creator_trx_id:创建这个 ReadView 的事务ID。

在这里插入图片描述

如果当前事务的 creator_trx_id 想要读取某个行记录,这个行记录ID 的trx_id ,这样会有以下的情况:

  1. 如果trx_id < 活跃的最小事务ID(up_limit_id),也就是说这个行记录在这些活跃的事务创建前就已经提交了,那么这个行记录对当前事务是可见的。
  2. 如果 trx_id > 活跃的最大事务ID(low_limit_id),这个说明行记录在这些活跃的事务之后才创建,说明这个行记录对当前事务是不可见的。
  3. 如果 up_limit_id < trx_id <low_limit_id ,说明该记录需要在 trx_ids 集合中,可能还处于活跃状态,因此我们需要在 trx_ids 集合中遍历 ,如果trx_id 存在于 trx_ids 集合中,证明这个事务 trx_id 还处于活跃状态,不可见,否则 ,trx_id 不存在于 trx_ids 集合中,说明事务trx_id 已经提交了,这行记录是可见的。

4.5 查询一条记录的具体过程?

  1. 获取事务自己的版本号,即 事务ID
  2. 获取 Read View
  3. 查询得到的数据,然后 Read View 中的事务版本号进行比较。
  4. 如果不符合 ReadView 规则, 那么就需要 UndoLog 中历史快照;
  5. 最后返回符合规则的数据

InnoDB 实现多版本控制 (MVCC)是通过 ReadView+ UndoLog 实现的

UndoLog 保存了历史快照,ReadView 规则帮助判断当前版本的数据是否可见。

4.6 总结

如果事务隔离级别是 ReadCommit ,一个事务的每一次 Select 都会去查一次ReadView ,每次查询的Read View 不同,就可能会造成不可重复读或者幻读的情况。

如果事务的隔离级别是可重读,为了避免不可重读读,一个事务只在第一次 Select 的时候会获取一次Read View ,然后后面索引的Select 会复用这个 ReadView.

转载地址:https://blog.csdn.net/weixin_40597409/article/details/115369183 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:mysql数据库 | 数据库优化
下一篇:JVM原理 | 逃逸分析

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月16日 16时23分08秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

【Leetcode刷题篇】剑指offer51 数组中的逆序对 2019-04-26
【Leetcode刷题篇】剑指offer55-平衡二叉树 2019-04-26
【Leetcode刷题篇】leetcode98 判断一棵树是否为二叉搜索树 2019-04-26
Java中arraylist和数组的相互转换 2019-04-26
【Leetcode刷题篇 】leetcode147 对链表进行插入排序 2019-04-26
【Leetcode刷题篇】leetcode148 排序链表 2019-04-26
【面试篇】Java中String、StringBuilder与StringBuffer的区别? 2019-04-26
【面试篇】Java对象的hashCode()相同,equals()一定为true吗? 2019-04-26
【面试篇】Java中static和final关键字的作用是什么? 2019-04-26
【面试篇】Java中接口和抽象类的区别是什么? 2019-04-26
【Java网络编程与IO流】Java中IO流分为几种?字符流、字节流、缓冲流、输入流、输出流、节点流、处理流 2019-04-26
【Java网络编程与IO流】Java中BIO、NIO、AIO的区别是什么? 2019-04-26
【Leetcode刷题篇】leetcode136 只出现一次的数字 2019-04-26
spring boot整合thymeleaf,支持JSP和HTML页面开发 2019-04-26
【Java网络编程与IO流】Spring boot整合SSE实现服务器实时推送流信息 2019-04-26
【Java网络编程与IO流】SpringBoot + WebSocket + Netty实现实时的服务器消息推送 2019-04-26
【Leetcode刷题篇】leetcode141 环形链表II 2019-04-26
【Leetcode刷题篇】leetcode160 相交链表 2019-04-26
【Leetcode刷题篇】leetcode169 多数元素 2019-04-26
【Leetcode刷题篇】leetcode461 汉明距离 2019-04-26