MySQL MVCC机制深入

2024-06-01 01:58

本文主要是介绍MySQL MVCC机制深入,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

MySQL MVCC机制

什么是MVCC

MVCC (Multiversion Concurrency Control) 中文全程叫多版本并发控制,是现代数据库(包括 MySQLOraclePostgreSQL 等)引擎实现中常用的处理读写冲突的手段,目的在于提高数据库高并发场景下的吞吐性能

如此一来不同的事务在并发过程中,SELECT 操作可以不加锁而是通过 MVCC 机制读取指定的版本历史记录,并通过一些手段保证保证读取的记录值符合事务所处的隔离级别,从而解决并发场景下的读写冲突。

InnoDB 中的 MVCC

  1. MySQLInnoDB 引擎支持 MVCC
  2. 应对高并发事务, MVCC 比单纯的加行锁更有效, 开销更小
  3. MVCC 在读已提交(Read Committed)和可重复读(Repeatable Read)隔离级别下起作用
  4. MVCC 既可以基于乐观锁又可以基于悲观锁来实现

InnoDB MVCC 实现原理

begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第 一个操作InnoDB表的语句,事务才真正启动,才会向mysql申请事务ID,mysql内部是严格照事务的启动顺序来分配事务ID的事务ID依次递增

InnoDBMVCC 的实现方式为:每一行记录都有两个隐藏列:DATA_TRX_IDDATA_ROLL_PTR(如果没有主键,则还会多一个隐藏的主键列)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-51oF0K3l-1588519526486)(https://i.loli.net/2020/05/03/zj6l4avrf9VCNsI.png)]

  • DATA_TRX_ID

    记录最近更新这条行记录的事务 ID,大小为 6 个字节

  • DATA_ROLL_PTR

    表示指向该行回滚段(rollback segment)的指针,大小为 7 个字节,InnoDB 便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在 undo 中都通过链表的形式组织。

  • DB_ROW_ID

    行标识(隐藏单调自增 ID),大小为 6 字节,如果表没有主键,InnoDB 会自动生成一个隐藏主键,因此会出现这个列。另外,每条记录的头信息(record header)里都有一个专门的bitdeleted_flag)来表示当前记录是否已经被删除。

举例说明

有一张表:user字段有id,name

事务执行表:

#Transaction 100#Transaction 200#Transaction 300#selectRR-ReadViewRC-ReadView
begin;begin;begin;begin;
update user set name=‘zxl300’ where id=1;
commit;
select name from user where id=1;[100,200],300 rs:zxl300[100,200],300 rs:zxl300
update user set name=‘zxl1’ where id=1;
update user set name=‘zxl2’ where id=1;
select name from user where id=1;[100,200],300 rs:zxl300[100,200],300 rs:zxl300
commit;update user set name=‘zxl3’ where id=1;
update user set name=‘zxl4’ where id=1;
select name from user where id=1;[100,200],300 rs:zxl300[200],300 rs:zxl2
commit;

如何组织 Undo Log 链

  1. userid=1 的这行记录加排他锁。
  2. 把该行原本的值拷贝到 undo log 中,DB_TRX_IDDB_ROLL_PTR 都不动
  3. 修改该行的值这时产生一个新版本,更新 DATA_TRX_ID 为修改记录的事务 ID,将 DATA_ROLL_PTR 指向刚刚拷贝到 undo log 链中的旧版本记录,这样就能通过 DB_ROLL_PTR 找到这条记录的历史版本。如果对同一行记录执行连续的 UPDATEUndo Log 会组成一个链表,遍历这个链表可以看到这条记录的变迁
  4. 记录 redo log,包括 undo log 中的修改

那么 INSERTDELETE 会怎么做呢?其实相比 UPDATE 这二者很简单,INSERT 会产生一条新纪录,它的 DATA_TRX_ID 为当前插入记录的事务 IDDELETE 某条记录时可看成是一种特殊的 UPDATE,其实是软删,真正执行删除操作会在 commit 时,DATA_TRX_ID 则记录下删除该记录的事务 ID

DB_TRX_ID 只有insert/update/delete时创建

ReadView 生成规则

当执行查询SQL时会生成一致性视图ReadView,它由执行查询时所有未提交的事务ID数组(数组中最小的id为mid_id)和已创建的最大事务ID(max_id)组成,查询的数据结果需要跟ReadView做比对从而得到快照结果。

RU 下的 ReadView 生成

RU 隔离级别下,直接读取版本的最新记录就 OK,对于 SERIALIZABLE 隔离级别,则是通过加锁互斥来访问数据,因此不需要 MVCC 的帮助。因此 MVCC 运行在 RCRR 这两个隔离级别下,当 InnoDB 隔离级别设置为二者其一时,在 SELECT 数据时就会用到版本链

核心问题是版本链中哪些版本对当前事务可见?

InnoDB 为了解决这个问题,设计了 ReadView(可读视图)的概念。

RR 下的 ReadView 生成

RR 隔离级别下,每个事务 touch first read 时(本质上就是执行第一个 SELECT 语句时,后续所有的 SELECT 都是复用这个 ReadView,其它 update, delete, insert 语句和一致性读 snapshot 的建立没有关系),会将当前系统中的所有的活跃事务拷贝到一个列表生成ReadView

RC 下的 ReadView 生成

RC 隔离级别下,每个 SELECT 语句开始时,都会重新将当前系统中的所有的活跃事务拷贝到一个列表生成 ReadView。二者的区别就在于生成 ReadView 的时间点不同,一个是事务之后第一个 SELECT 语句开始、一个是事务中每条 SELECT 语句开始。

MVCC 判断流程

根据ReadView生成规则可以将数据分为三个区域,小于最小未提交事务Id的称为已提交事务大于未提交事务最小ID小于已创建最大事务ID称为未提交与已提交事务,大于已创建最大事务ID称为未开始事务

https://c01.gaitubao.net/gaitubao_Fla_tpH0xuEv1zaf9Ktn958KTlUs.png?watermark/1/image/aHR0cHM6Ly9jMDEuZ2FpdHViYW8ubmV0L2dhaXR1YmFvX0ZvNlZaakFzb3lCN1ZOVU5zYXQ3cVJMNEt4QzAucG5nP2ltYWdlTW9ncjIvcm90YXRlLzA=/gravity/Center/dx/0/dy/0
  1. 如果ROW的ID(DB_TRX_ID)落在绿色部分(DB_TRX_ID<mid_id),表示这个版本是已提交的事务生成的,这个数据是可见的。
  2. 如果ROW的ID(DB_TRX_ID)落在红色部分(DB_TRX_ID>max_id),表示这个版本是由将来启动事务生成的,是肯定不可见的。
  3. 如果ROW的ID(DB_TRX_ID)落在黄色部分(mid_id<=DB_TRX_ID<=max_id),则分为两种情况:
    • DB_TRX_ID未提交的事务ID数组,表示这个版本是由还没有提交的事务生成的,不可见,当前自己的事物是可见的。
    • DB_TRX_ID不在未提交的事务ID数组,表示这个版本是已提交的事务生成的,可见。

对于删除的情况可以认为是update的特殊情况,会将版本链是最新的数据复制一份,然后将DB_TRX_ID修改成删除操作的DB_TRX_ID,同时在该条记录的头信息(record header)里(deleted flag)标记位写上ture,来表示当前记录已被删除,在查询时按照上而的规则查询到对应的记录如果deleted flag标记位为ture,意味着记录已被删除,则不返回数据。

例RR MVCC 判断流程

  1. user 表中存在一条用户信息,id:1 name:zxl.

  2. 首先#Transaction 100,#Transaction 200, #Transaction 300及#select同时开启事务。

  3. #Transaction 300更新name为zxl300并提交。

  4. #select进行查询操作,首次查询会创建ReadView,创建ReadView为[100,200],300,根据MVCC判断流程进行判断,当前记录链路DB_TRX_ID为300,300的区间应该在未提交及已提交范围,300不在未提交数组范围,所以本条记录是可见的,即返回zxl300.

  5. 而后#Transaction 100,进行了两次更新update user set name='zxl1' where id=1;update user set name='zxl2' where id=1;

  6. #select再次进行查询select name from user where id=1;,因为事务隔离级别为RR所以ReadView依然为[100,200],300,当前记录链路DB_TRX_ID为 100 ,100的区间应该在未提交及已提交范围,100在未提交数组范围,因此本条记录不可见,根据链路继续查询,发现还是100,则继续链路查询发现为300发现不在未提交范围内则返回zxl300

这篇关于MySQL MVCC机制深入的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1019669

相关文章

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

mysql出现ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的解决方法

《mysql出现ERROR2003(HY000):Can‘tconnecttoMySQLserveron‘localhost‘(10061)的解决方法》本文主要介绍了mysql出现... 目录前言:第一步:第二步:第三步:总结:前言:当你想通过命令窗口想打开mysql时候发现提http://www.cpp

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

MySQL错误代码2058和2059的解决办法

《MySQL错误代码2058和2059的解决办法》:本文主要介绍MySQL错误代码2058和2059的解决办法,2058和2059的错误码核心都是你用的客户端工具和mysql版本的密码插件不匹配,... 目录1. 前置理解2.报错现象3.解决办法(敲重点!!!)1. php前置理解2058和2059的错误

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE

MySQL Workbench 安装教程(保姆级)

《MySQLWorkbench安装教程(保姆级)》MySQLWorkbench是一款强大的数据库设计和管理工具,本文主要介绍了MySQLWorkbench安装教程,文中通过图文介绍的非常详细,对大... 目录前言:详细步骤:一、检查安装的数据库版本二、在官网下载对应的mysql Workbench版本,要是

mysql数据库重置表主键id的实现

《mysql数据库重置表主键id的实现》在我们的开发过程中,难免在做测试的时候会生成一些杂乱无章的SQL主键数据,本文主要介绍了mysql数据库重置表主键id的实现,具有一定的参考价值,感兴趣的可以了... 目录关键语法演示案例在我们的开发过程中,难免在做测试的时候会生成一些杂乱无章的SQL主键数据,当我们

浅谈mysql的sql_mode可能会限制你的查询

《浅谈mysql的sql_mode可能会限制你的查询》本文主要介绍了浅谈mysql的sql_mode可能会限制你的查询,这个问题主要说明的是,我们写的sql查询语句违背了聚合函数groupby的规则... 目录场景:问题描述原因分析:解决方案:第一种:修改后,只有当前生效,若是mysql服务重启,就会失效;

MySQL多列IN查询的实现

《MySQL多列IN查询的实现》多列IN查询是一种强大的筛选工具,它允许通过多字段组合快速过滤数据,本文主要介绍了MySQL多列IN查询的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录一、基础语法:多列 IN 的两种写法1. 直接值列表2. 子查询二、对比传统 OR 的写法三、性能分析与优化1.