点击蓝色“架构文摘”关注我哟

加个“星标”,每天上午 09:25,干货推送!

来源:https://segmentfault.com/a/1190000037557620
作者:白菜1031

一、什么是多版本并发控制

多版本并发控制 技术的英文全称是 Multiversion Concurrency Control ,简称 MVCC

多版本并发控制(MVCC)
是通过保存数据在某个时间点的快照来实现并发控制的。也就是说,不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。

简单来说, 多版本并发控制
的思想就是保存数据的历史版本,通过对数据行的多个版本管理来实现数据库的并发控制。这样我们就可以通过比较版本号决定数据是否显示出来,读取数据的时候不需要加锁也可以保证事务的隔离效果。

可以认为 多版本并发控制(MVCC)
是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。

MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不仅是MySQL,包括Oracle、PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一的实现标准,典型的有乐观(optimistic)并发控制悲观(pessimistic)并发控制

二、多版本并发控制解决了哪些问题

1. 读写之间阻塞的问题

通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。

提高并发的演进思路:

  • 普通锁,只能串行执行;

  • 读写锁,可以实现读读并发;

  • 数据多版本并发控制,可以实现读写并发。

2. 降低了死锁的概率

因为 InnoDB 的 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。

3. 解决一致性读的问题

一致性读也被称为 快照读
,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

三、快照读与当前读

快照读(SnapShot Read) 是一种 一致性不加锁的读 ,是 InnoDB并发如此之高的核心原因之一

这里的 一致性 是指,事务读取到的数据,要么是 事务开始前就已经存在的数据 ,要么是 事务自身插入或者修改过的数据

不加锁的简单的 SELECT 都属于 快照读 ,例如:

    `SELECT * FROM t WHERE id=1`

快照读 相对应的则是 当前读当前读 就是读取最新数据,而不是历史版本的数据。加锁的 SELECT 就属于当前读,例如:

    SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE;

    SELECT * FROM t WHERE id=1 FOR UPDATE;

四、InnoDB 的 MVCC 是如何工作的

1. InnoDB 是如何存储记录的多个版本的

事务版本号

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

行记录的隐藏列

InnoDB 的叶子段存储了数据页,数据页中保存了行记录,而在行记录中有一些重要的隐藏字段:

  • DB_ROW_ID :6-byte,隐藏的行 ID,用来生成默认聚簇索引。如果我们创建数据表的时候没有指定聚簇索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚簇索引的方式可以提升数据的查找效率。

  • DB_TRX_ID :6-byte,操作这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。

  • DB_ROLL_PTR :7-byte,回滚指针,也就是指向这个记录的 Undo Log 信息。

InnoDB数据记录隐藏列
Undo Log

InnoDB 将行记录快照保存在了 Undo Log 里,我们可以在回滚段中找到它们,如下图所示:

Undo Log回滚历史记录

从图中能看到回滚指针将数据行的所有快照记录都通过链表的结构串联了起来,每个快照的记录都保存了当时的 db_trx_id,也是那个时间点操作这个数据的事务
ID。这样如果我们想要找历史快照,就可以通过遍历回滚指针的方式进行查找。

2. 在 可重复读(REPEATABLE READ) 隔离级别下, InnoDB 的 MVCC 是如何工作的

查询(SELECT)

InnoDB 会根据以下两个条件检查每行记录:

  1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以 确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的

  2. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保 事务读取到的行,在事务开始之前未被删除

只有符合上述两个条件的记录,才能返回作为查询结果。

插入(INSERT)

InnoDB为新插入的每一行保存当前系统版本号作为行版本号。

删除(DELETE)

InnoDB为删除的每一行保存当前系统版本号作为行删除标识。 
删除在内部被视为更新,行中的一个特殊位会被设置为已删除。

更新(UPDATE)

InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

五、总结

多版本并发控制(MVCC) 在一定程度上实现了 读写并发 ,它只在 可重复读(REPEATABLE READ)提交读(READ COMMITTED) 两个隔离级别下工作。其他两个隔离级别都和 MVCC 不兼容,因为 未提交读(READ UNCOMMITTED) ,总是读取最新的数据行,而不是符合当前事务版本的数据行。而 可串行化(SERIALIZABLE)
则会对所有读取的行都加锁。

行锁,并发,事务回滚 等多种特性都和MVCC相关。

参考

MySQL5.7文档:innodb-multi-versioning  
《高性能MySQL》

end

推荐阅读:

  • TCP 三次握手、四手挥手,这样说你能明白吧!

  • 拜托,不要再问我线程池啦!

  • 为什么 Redis 单线程还这么快?

  • Spring Cloud架构的各个组件的原理分析

  • 一口气说出 5 种 IO 模型,蒙圈了!

如有收获,点个在看,诚挚感谢

实现mvcc_MySQL 的多版本并发控制(MVCC) 是干啥的?相关推荐

  1. MySQL的多版本并发控制(MVCC)

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:又一程序员进了ICU:压垮一个家庭,一张结算单就够 个人原创100W+访问量博客:点击前往,查看更多 来源:ht ...

  2. pg事务篇(一)—— 事务与多版本并发控制MVCC

    一. MVCC常用实现方法 一般MVCC有2种实现方法: 写新数据时,把旧数据快照存入其他位置(如oracle的回滚段.sqlserver的tempdb).当读数据时,读的是快照的旧数据. 写新数据时 ...

  3. Mysql事务隔离级别及MVCC(多版本并发控制)

    一.MySQL事务隔离级别 先注明一点:以下讨论都是在多事务并发的情境下讨论的 事务的特性(InnoDB引擎才有事务): ACID 原子性:一个事务不可再分割,要么都执行要么都不执行 一致性:一个事务 ...

  4. MySQL 高级 —— MVCC 多版本并发控制

    引言 MySQL的大多数事务型存储引擎实现的都不是简单的行级锁.基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制--MVCC.包括其他数据库如Oracle等,由于MVCC并没有一个统一的实现 ...

  5. mysql事务并发控制_mysql事务和多版本并发控制详解

    一.mysql事务 事务就是一组原子性的SQL查询,或者说一个独立的工作单元.如果数据库引擎可以成功执行该组全部语句,那么就执行该组语句.如果其中有任何一条语句不能执行,那么所有的语句都不会执行.也就 ...

  6. MySQL中的MVVC多版本并发控制机制

    文章目录 引入 MySQL中MVCC的运用 快照读和当前读 更多文章和干货 引入 MVCC全称是:Multiversion concurrency control,多版本并发控制,提供并发访问数据库时 ...

  7. MYSQL专题-MVCC多版本并发控制

    MVCC,全称Multi-Version Concurrency Control,即多版本并发控制.MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内 ...

  8. fetch first mysql_MySQL多版本并发控制机制(MVCC)源码浅析

    MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<>诚然讲的非常透彻,但只能提纲挈领,不能让 ...

  9. 【SQL】MVCC 多版本并发控制

    MVCC多版本并发控制 快照读与当前读 隔离级别 隐藏字段,undo log 版本链 隐藏字段trx_id 版本链 read view 举例说明 read committed(读已提交)隔离级别下 r ...

最新文章

  1. 【Leetcode | easy】回文数
  2. React Native ios打包
  3. 2011年9月最新整理的10个有趣的jQuery插件集合
  4. 怎样才能到国外做博士后
  5. github 国内加速镜像
  6. java note项目_Java Request.setNote方法代码示例
  7. 3.7.5 - Modifying Strings
  8. xm文件转换为mp3_怎么才能将M4A转换为MP3?秘籍公开
  9. 计算机教室戴尔电脑网络同传,如何进入和使用网络同传功能
  10. Java 从入门到放弃?
  11. Win11此应用无法在你的电脑上运行怎么解决
  12. QQ5.1 去广告显IP新春完美版【木子李作品】
  13. 用Python画动态圣诞树 学会了送给你女朋友呀~
  14. C++ 中打开 exe 文件
  15. 哈工大软件构造期末知识点总结
  16. 证券交易买进卖出手续费公式
  17. 文本分析 | 年报转换TXT关键词频统计
  18. Linux中创建快捷方式
  19. smart200PLC TCP通讯
  20. unity 基于FGUI编辑器导出的图集切图工具

热门文章

  1. ICBU可控文本生成技术详解
  2. 获国际架构顶会ATC2021最佳论文!Fuxi2.0去中心化的调度架构详解
  3. 重温设计模式之 Factory
  4. 一文详解Serverless架构模式
  5. 全球独家 | 赋予企业级开源无限可能,阿里云首发云数据库MongoDB 4.2版本
  6. Spark内置图像数据源初探
  7. 图(关系网络)数据分析及阿里应用
  8. 【开源】Tsar——灵活的系统和应用采集软件
  9. MaxCompute Studio使用心得系列6——一个工具完成整个Python UDF开发
  10. stack vs heap:栈区分配内存快还是堆区分配内存快 ?