MySQL MVCC详解

目录

 前言

MVCC实现原理

UndoLog版本链

 ReadView

MVCC是否可以解决不可重复读与幻读

隔离级别

READ UNCOMMITTED - 读未提交与脏读

READ COMMITTED - 读已提交与不可重复读

REPEATABLE READ - 可重复读与幻读

SERIALIZABLE - 串行化

小结


 前言

    为了提高数据库并发能力,首先应该想到的就是多线程,但是多线程带来的线程安全问题又不得不考虑。通常采用加锁解决,但这个锁的粒度和锁策略是至关重要的。MVCC全称MultiVersioned ConcurrencyControl(多版本并发控制),MVCC使用了锁、UndoLog以及ReadView配合来完成这件事情。

MVCC实现原理

UndoLog版本链

    MVCC的实现是基于 Undo Log 版本链和 ReadView 来完成的,Undo Log做为回滚的基础,在执行Update或Delete操作时,会将每次操作的上一个版本记录在Undo Log中,每条Undo Log中都记录⼀个叫做 roll_pointer 的引用信息,通过 roll_pointer 就可以将某条数据对应的Undo Log组织成⼀个Undo链,在数据行的头部通过数据行中的 roll_pointer 与Undo Log中的第⼀条日志进行关联,这样就构成一条完整的数据版本链。

    每一条被修改的记录都会有一条版本链,体现了这条记录的所有变更,当有事务对这条数据进行修改时,将修改后的数据链接到版本链接的头部。

 ReadView

    每条数据的版本链都构造好之后,在查询时具体选择哪个版本呢?这⾥就需要使用 ReadView 结构来实现了,所谓 ReadView 是⼀个内存结构,顾名思义是⼀个视图,在事务使用 select 查询数据时就会构造⼀个ReadView,里面记录了该版本链的一些统计值,这样在后续查询处理时就不用遍历所有版本链了,这些统计值具体包括:

  • m_ids :当前所有活跃事务的集合(活跃事务:未提交的事务)
  • m_low_limit_id :活跃事务集合中最小事务Id
  • m_up_limit_id :下⼀个将被分配的事务Id,也就是 版本链头的事务Id + 1 
  • m_creator_trx_id :创建当前 ReadView 的事务Id

    构造好 ReadView 之后需要根据一定的查询规则找到唯一的可用版本,会在UndoLog版本连中找到select查询具体记录版本的链头,从链头开始遍历所有版本,根据四步查找规则,判断每个版本是否符合要求,如果某个版本符合要求则返回该版本数据。

  • 第一步:判断该版本是否为当前事务创建,若 m_creator_trx_id 等于该版本事务id,意味着读取自己修改的数据,可以直接访问,如果不等则到第二步
  • 第⼆步:若该版本事务Id < m_up_limit_id (最⼩事务Id),意味着该版本在ReadView⽣成之前已经提交,可以直接访问,如果不是则到第三步
  • 第三步:若该版本事务Id >=  m_low_limit_id (最大事务Id),意味着该版本在ReadView生成之后才创建,所以肯定不能被当前事务访问,因为该条记录无法判断是否提交,所以无需第四步判断,直接遍历下一个版本,如果不是则到第四步
  • 第四步:若该版本事务Id在 m_up_limit_id (最小事务Id)和 m_low_limit_id (最大事务Id)之间,同时该版本不在活跃事务列表中,意味着创建ReadView时该版本已经提交,可以直接访问,如果不是则遍历并判断下一个版本

    这样从版本链头遍历判断到版本链尾,找到⾸个符合要求的版本即可,就可以实现查询到的结果都是已经提交事务的数据,那么就可以解决脏读问题。

MVCC是否可以解决不可重复读与幻读

  • ⾸先幻读无法通过MVCC单独解决,InnoDB在可重复读隔离级别下使用临建锁,锁住某条记录以及该记录之前的间隙,可以解决大部分幻读问题,但无法从根本上解决幻读问题
  • 对于不可重复读问题,在事务中的第⼀个查询时创建⼀个ReadView,后续查询都是⽤这个ReadView进行判断,所以每次的查询结果都是一样的,从而解决不可重复读问题,在REPEATABLE READ 可重复读,隔离级别下就采用的这种方式
  • 如果事务每次查询都创建⼀个新的ReadView,这样就会出现不可重复读问题,因为不同的ReadView中参数可能不一致,那么在UndoLog中找到的数据就可能不一致,在 READ COMMITTED 读已提交的隔离级别下就是这种实现方式

    这些机制加上锁就可以实现MySQL的四种隔离性 

隔离级别

READ UNCOMMITTED - 读未提交与脏读

实现方式

  • 读取时:不加任何锁,直接读取版本链中的最新版本,也就是当前读,可能会出现脏读,不可重复读、幻读问题
  • 更新时:加共享行锁(S锁),事务结束时释放,在数据修改完成之前,其他事务不能修改当前数据,但可以被其他事务读取

存在问题 

    事务的 READ UNCOMMITTED 隔离级别不使用独占锁,所以并发性能很高,但是会出现大量的数据安全问题。

    比如在事务A中执行了一条 INSERT 语句,在没有执行 COMMIT 的情况下,会在事务B中被读取到,此时如果事务A执行回滚操作,那么事务B中读取到事务A写入的数据将没有意义,这个现象叫做 "脏读"。

注意:

    由于 READ UNCOMMITTED 读未提交会出现"脏读"现象,在正常的业务中出现这种问题会产生非常危重后果,所以正常情况下应该避免使用 READ UNCOMMITTED 读未提交这种的隔离级别。

READ COMMITTED - 读已提交与不可重复读

实现方式

  • 读取时:不加锁,但使⽤快照读,即按照 MVCC 机制读取符合 ReadView 要求的版本数据,每次查询都会构造一个新的 ReadView ,可以解决脏读,但无法解决不可重复读和幻读问题
  • 更新时:加独占行锁(X),事务结束时释放,数据在修改完毕之前,其他事务不能修改也不能读取这行数据

存在问题

    为了解决脏读问题,可以把事务的隔离级别设置为 READ COMMITTED ,这时事务只能读到了其他事务提交之后的数据,但会出现不可重复读的问题,核心原因就是每次快照读都会构造新的ReadView。

    比如事务A先对某条数据进行了查询,之后事务B对这条数据进行了修改,并且提交( COMMIT )事务,事务A再对这条数据进行查询时,得到了事务B修改之后的结果,这导致了事务A在同一个事务中以相同的条件查询得到了不同的值,这个现象要"不可重复读"。

REPEATABLE READ - 可重复读与幻读

实现方式

  • 读取时:不加锁,也使⽤快照读,按照MVCC机制读取符合ReadView要求的版本数据,但无论事务中有几次查询,只会在⾸次查询时生成一个ReadView,可以解决脏读、不可重复读,配合Next-Key行锁可以解决一部分幻读问题
  • 更新时:加Next-Key行锁,事务结束时释放,在一个范围内的数据修改完成之前,其他事务不能对这个范围内的数据进行修改、插入和删除操作,同时也不能被查询

存在问题

    事务的 REPEATABLE READ 隔离级别是会出现幻读问题的,在 InnoDB 中使用了Next-Key行锁来解决大部分场景下的幻读问题,那么在不加 Next-Key 行锁的情况下会出现什么问题吗?

    使用 Next-Key 锁,锁住的是当前索引记录以及索引记录前面的间隙,那么在不加 NextKey 锁的情况下,也就是只对当前修改行加了独占行锁(X),这时记录前的间隙没有被锁定,其他的事务就可以向这个间隙中插入记录,就会导致一个问题如下:

    比如事务A查询了一个区间的记录得到结果集A,事务B向这个区间的间隙中写入了⼀条记录,事务A再查询这个区间的结果集时会查到事务B新写入的记录得到结果集B,两次查询的结果集不一致,这个现象就是"幻读“ 。

SERIALIZABLE - 串行化

实现方式

  • 读取时:加共享表锁,读取版本链中的最新版本,事务结束时释放
  • 更新时:加独占表锁,事务结束时释放,完全串行操作,可以解决所有事务问题

存在问题 

    所有的更新都是串行操作,效率极低

小结

    当理解了不同隔离级别下实现原理与所存在的问题时,可以修改数据库隔离级别进行问题重现,理论与实操结合,相信你会有很大收获。

这里提供一些可能会用到的命令

# 开启事务
START TRANSACTION;
# 提交事务
commit;
# 回滚事务
rollback;
# 查看全局作用域隔离级别
SELECT @@GLOBAL.transaction_isolation;
# 查看会话作用域隔离级别
SELECT @@SESSION.transaction_isolation;
# 通过GLOBAL|SESSION分别指定不同作用域下隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;
# 隔离级别level: 
    REPEATABLE READ # 可重复读
    READ COMMITTED # 读已提交
    READ UNCOMMITTED # 读未提交
    SERIALIZABLE # 串⾏化
# 访问模式access_mode: 
    READ WRITE # 表⽰事务可以对数据进⾏读写
    READ ONLY # 表⽰事务是只读,不能对数据进⾏读写

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/743306.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux网络(一)初识计算机网络】

一、网络发展 1.发展背景 2.发展类型 二、网络协议 1.认识协议 2.协议分层 3.OSI七层模型 4.TCP/IP协议 三、网络传输 1.协议报头 2.局域网内的两台主机通信 3.跨网络的两台主机通信 四、网络地址 1.IP地址 2.MAC地址 一、网络发展 1.发展背景 计算机网络的发展…

12 学习总结:操作符

目录 一、操作符的分类 二、二进制和进制转换 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;二进制 &#xff08;三&#xff09;进制转换 1、2进制与10进制的互换 &#xff08;1&#xff09;2进制转化10进制 &#xff08;2&#xff09;10进制转化2进制 2…

手机照片回收站无法恢复图片怎么办?2个措施,找回丢失的相册

我们的照片和视频存储在各种设备中&#xff0c;其中包括我们的智能手机&#xff0c;但有时候我们可能会不小心删除这些重要的数据。这时&#xff0c;手机照片回收站就显得尤为重要。然而&#xff0c;即使有回收站&#xff0c;我们也可能遇到一些问题&#xff0c;例如回收站中的…

Geoserver源码解读四 REST服务

文章目录 文章目录 一、概要 二、前置知识点-FreeMarker 三、前置知识点-AbstractHttpMessageConverter 3.1 描述 3.2 应用 四、前置知识点-AbstractDecorator 4.1描述 4.2 应用 五、工作空间查询解读 5.1 模板解读 5.2 请求转换器解读 一、概要 关于geoserver的r…

嘉立创EDA验证板复制下载

https://item.szlcsc.com/777208.html https://oshwhub.com/li-chuang-zhi-neng-ying-jian-bu/dc-dc在这里插入图片描述

还能这样执行命令?命令执行绕过及防护规则研究

一、引言 我是渗透工程师->很多小伙伴在做攻防实战时发现有时在命令执行的payload中穿插单双引号命令也能执行成功&#xff0c;有时却又不行。那么到底在什么条件下用什么样的方式能实现对命令的切分呢&#xff1f;其中的原理又是如何&#xff1f;有没有其他绕过方式&#…

常见的跨域场景

我们在解决一个问题的时候应该先去了解这个问题是如何产生的&#xff0c;为什么会有跨域的存在呢&#xff1f;其实&#xff0c;最终的罪魁祸首都是浏览器的同源策略&#xff0c;浏览器的同源策略限制我们只能在相同的协议、IP地址、端口号相同&#xff0c;如果有任何一个不通&a…

音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析

一、引言 FFmpeg源码中 通过h264_parse_nal_header函数将H.264码流的NALU Header解析出来。下面对h264_parse_nal_header函数进行分析。 二、h264_parse_nal_header函数定义 h264_parse_nal_header函数定义在FFmpeg源码&#xff08;下面演示的FFmpeg源码版本是5.0.3&#xff…

【Linux】进程间通信_4

文章目录 七、进程间通信1. 进程间通信分类systeam V共享内存消息队列 未完待续 七、进程间通信 1. 进程间通信分类 systeam V共享内存 进程间通信的本质就是让不同进程看到同一份资源。而systeam V是通过让不同的进程经过页表映射到同一块内存空间&#xff08;操作系统完成…

Verilog描述一个带有异步置位和异步清零的D触发器

1 带有异步置位和异步清零的D触发器的真值表&#xff1a; 2 Verilog代码描述 module DFF_SR(CLK, D, Rd, Sd, Q, QN);input CLK, D, Rd, Sd;output Q, QN;reg Q_DFF;always (posedge CLKor negedge Rd or negedge Sd)beginif(!Rd)Q_DFF < 1b0;else if(!Sd)Q_DFF < 1b1;e…

硬盘监控和分析工具:Smartctl

文章目录 1. 概述2. 安装3. 使用4. smartctl属性信息介绍 1. 概述 Smartctl&#xff08;S.M.A.R.T 自监控&#xff0c;分析和报告技术&#xff09;是类Unix系统下实施SMART任务命令行套件或工具&#xff0c;它用于打印SMART自检和错误日志&#xff0c;启用并禁用SMRAT自动检测…

永洪bi知识点

1、下拉过滤组件和下拉参数组件的区别 下拉过滤组件只能对跟他绑有相同数据集的组件进行过滤 而下拉参数组件是当你设置了筛选条件以后&#xff0c;那么所有的组件&#xff0c;不管你绑定了什么样子的数据集&#xff0c;都能起作用&#xff0c;前提是你这个组件是需要去绑定参…

docker基础使用教程

1.准备工作 例子&#xff1a;工程在docker_test 生成requirements.txt文件命令&#xff1a;&#xff08;使用参考链接2&#xff09; pip list --formatfreeze > requirements.txt 参考链接1&#xff1a; 安装pipreqs可能比较困难 python 项目自动生成环境配置文件require…

图片如何去水印,分享4个小妙招,手把手教会你!

作为一个经常逛社区网站下载表情包、头像的人&#xff0c;遇到的一个大难题就是图片有水印。如何才能快速去除水印&#xff1f;询问了一圈身边朋友&#xff0c;搜集了各种资料&#xff0c;小编整理了4个超好用的方法。 如果大家和小编一样&#xff0c;能坐着就不站着&#xff0…

【C++LeetCode】【热题100】移动零【简单】-不同效率的题解【4】

题目&#xff1a; 暴力方法&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int counts0;//零的数量int swapCounts0;for(int i0;i<nums.size();i){if(nums[i]0){counts1;}}swapCountscounts;for(int i0;i<nums.size();i){fo…

强制用户裸奔,微软新规以后用Win11必须先注册了

想必在座资深搞机佬对 Windows「本地账户」这一大功能都不陌生。 简单来说&#xff0c;本地用户账户是过去在安装操作系统时自动为大家创建的用于系统管理的默认内置账户。 「本地」二字&#xff0c;代表了即便在电脑不联网的情况下也能自动创建成功。 更重要的是它不与 Micr…

优化了自制的浏览器主页的全屏功能

第一次修改 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>全屏功能</title><style>…

关于window的安装

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 第一windows的分类 旗舰版 个人版…

2024最新算法:北极海鹦优化(Arctic puffin optimization,APO)算法求解23个函数,MATLAB代码

一、算法介绍 北极海鹦优化&#xff08;Arctic puffin optimization&#xff0c;APO&#xff09;算法是2024年提出一种智能优化算法。该算法模拟海鹦在空中飞行和水下觅食两个阶段的行为&#xff0c;旨在实现勘探与开发之间更好的平衡。该算法包括几个关键操作&#xff0c;包括…

张家界首迎新V系客车!苏州金龙携手中信旅运打造旅游客运新范本

在国际张&#xff08;张家界&#xff09;这片美丽而又神奇的的土地上&#xff0c;伴随着旅游市场持续火爆的&#xff0c;是旅游客运行业的激烈竞争。 如何在旅游市场的近乎“白刃战”中突出重围&#xff0c;成为众多旅游运输企业亟待破解的一道难题。然而&#xff0c;作为张家…