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

相关文章

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

oracle DBMS_SQL.PARSE的使用方法和示例

《oracleDBMS_SQL.PARSE的使用方法和示例》DBMS_SQL是Oracle数据库中的一个强大包,用于动态构建和执行SQL语句,DBMS_SQL.PARSE过程解析SQL语句或PL/S... 目录语法示例注意事项DBMS_SQL 是 oracle 数据库中的一个强大包,它允许动态地构建和执行

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I

在MySQL执行UPDATE语句时遇到的错误1175的解决方案

《在MySQL执行UPDATE语句时遇到的错误1175的解决方案》MySQL安全更新模式(SafeUpdateMode)限制了UPDATE和DELETE操作,要求使用WHERE子句时必须基于主键或索引... mysql 中遇到的 Error Code: 1175 是由于启用了 安全更新模式(Safe Upd

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySql死锁怎么排查的方法实现

《MySql死锁怎么排查的方法实现》本文主要介绍了MySql死锁怎么排查的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录前言一、死锁排查方法1. 查看死锁日志方法 1:启用死锁日志输出方法 2:检查 mysql 错误

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

MySQL修改密码的四种实现方式

《MySQL修改密码的四种实现方式》文章主要介绍了如何使用命令行工具修改MySQL密码,包括使用`setpassword`命令和`mysqladmin`命令,此外,还详细描述了忘记密码时的处理方法,包... 目录mysql修改密码四种方式一、set password命令二、使用mysqladmin三、修改u