当前位置:

首页 > 编程开发 > MySQL MVVC多版本并发控制如何实现

MySQL MVVC多版本并发控制如何实现

一、概述MVCC(MultiversionConcurrencyControl),多版本并发控制。它和undolog中的版本链息息相关,MVVC通过数据行的多个版本来实现数据库的并发控制。简单的说就是当前事务查询另一个事务正在更改的行(如果此时读取就会发生脏读),不用加锁等待,而是读取该数据的历史版本,降低响应时间。MVVC是通过undolog和ReadView两种技术实现的。二、快照读与当前读MVCC在MySQLInnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读

本文目录

    一、概述

    MVCC(Multiversion Concurrency Control),多版本并发控制。它和undo log中的版本链息息相关,MVVC通过数据行的多个版本来实现数据库的并发控制。

    简单的说就是当前事务查询另一个事务正在更改的行(如果此时读取就会发生脏读),不用加锁等待,而是读取该数据的历史版本,降低响应时间。

    MVVC是通过undo log和Read View两种技术实现的。

    二、快照读与当前读

    MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读 ,而这个读指的就是快照读 , 而非当前读。当前读实际上是一种加锁的操作。

    1.当前读

    当前读读取的记录一定是最新的数据,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

    加锁的读被称为当前读,还有数据的增删改都是要先读取数据的,这一读取过程也是当前读。

    SELECT * FROM t LOCK IN SHARE MODE; # 共享锁
    SELECT * FROM t FOR UPDATE; # 排他锁
    UPDATE SET t..

    2.快照读

    快照读又叫一致性读,读取的是数据行的快照版本。在MySQL中,普通的select语句(不加for update或lock in share mode的select语句)默认就是使用的快照读,不加锁。

    SELECT * FROM table WHERE ...

    之所以这样,是因为快照读可以避免加锁操作,降低开销。

    当事务的隔离级别是串行时,快照读就没有用了,会退化为当前读。

    三、隔离级别与版本链复习

    隔离级别:

    在MySQL中默认的隔离级别就是可重复读RR,可以解决不可重复读问题,在MySQL中,特别的还额外支持解决幻读问题。

    它是如何解决幻读问题的呢?有两种方式:

    • 使用间隙锁和临键锁解决,简而言之就是加锁,在此期间其他事务不能够插入数据

    • MVCC方式,无需加锁,消耗低(缺点是没有完全解决幻读问题)。

    undo log版本链:

    对应InnoDB来说,聚簇索引中的每个记录都包含了两个必要的隐藏字段:

    • trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列。

    • roll_pointer:回滚指针,每次修改数据时,都会把旧数据放入undo log日志中,新的数据指向该旧数据,做成一个版本链,该指针字段就称为回滚指针,通过该指针可以找到修改前的数据。

    举例:

    有一个id为8的事务创建了一条数据,那么该记录的示意图大概如下:

    MySQL MVVC多版本并发控制如何实现

    假设之后两个id分别为10、20的事务对这条记录进行update操作,流程如下:

    事务10事务20
    BEGIN; 
     BEGIN;
    UPDATE student SET name='李四' WHERE id=1; 
    UPDATE student SET name='王五' WHERE id=1; 
    COMMIT; 
     UPDATE student SET name='赵六' WHERE id=1;
     UPDATE student SET name='钱七' WHERE id=1;
     COMMIT;

    每次修改都会生成一个undo log日志,每个日志都相互链接,构成版本链,此时该条数据的示意图如下:

    MySQL MVVC多版本并发控制如何实现

    每个版本中还包含生成该版本时对应的事务id 。

    四、Read View

    有了undo log就可以读取到记录的历史版本,那么在什么情况下,读取哪个版本的记录呢?这就用到了Read View,它帮我们解决了行的可见性问题。

    Read View就是当某个事务在使用MVVC机制进行快照读操作时产生的读视图。该视图是数据库当前所有活跃事务id(还未提交的事务)组成的列表的一个快照。

    1.实现原理

    四种隔离级别里,读未提交和串行化是不会使用MVVC的,因为读未提交直接读取某个数据的最新数据即可,串行化是通过加锁来读的。

    读已提交和可重复读都必须保证读到的数据都是其他事务提交了的,所以,其他事务修改了数据但是还未提交,我们不能够访问该数据,但可以通过MVVC机制读取该记录的历史版本,核心问题就是需要判断版本链中的哪条历史版本是当前事务可见的,这也是ReadView要解决的问题。

    Read View包含4个比较重要的内容:

    • creator_trx_id:创建这个Read View的事务id,Read View和事务是一一对应的。

    只有事务对表中的记录做修改时才会为事务分配事务id,否则一个事务中只有读操作,该事务的id默认为0。

    • trx_ids:表示在生成Read View时当前系统中活跃的事务id列表。提交了的事务不在其中。

    • up_limit_id:活跃的事务中最小的事务id。

    • low_limit_id:表示生成Read View时系统应该分配给下一个事务的id值,同样也表示系统中最大的事务id值。

    注意:low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为1, 2,5这三个事务,之后id为5的事务提交了。那么一个新的读事务在生成ReadView时, trx_ids就包括1和2,up_limit_id的值就是1,low_limit_id的值就是6。

    MySQL MVVC多版本并发控制如何实现

    2.Read View规则

    MySQL MVVC多版本并发控制如何实现

    版本链

    当某个事务有了Read View,访问某条记录时,需要按照下面的步骤判断该记录的哪个版本可见:

    • 如果该版本记录的trx_id和Read View的creator_trx_id相同,意味着该版本的记录是由当前事务修改的,因此该版本可以被当前事务访问

    • 如果该版本记录的trx_id小于Read View的up_limit_id,证明当前事务生成Read View时,此事务已经提交了,所以当前事务可以读取该版本。

    • 如果该版本的trx_id大于等于low_limit_id,证明生成该版本的事务在当前事务生成Read View之后才开启,所以该版本不可以被当前事务访问。

    • 如果被访问版本的trx_id属性值在ReadView的up_limit_id和low_limit_id之间,那就需要判断一下trx_id属性值是不是在trx_ids列表中,如果不在的话才能访问,否则不能访问。

    3.整体流程

    了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:

    • 首先获取事务自己的版本号,也就是事务ID;

    • 获取 ReadView;

    • 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;

    • 如果不符合 ReadView 规则,就需要从Undo Log中获取历史快照;

    • 最后返回符合规则的数据。

    在隔离级别为读已提交时,一个事务中的每一次SELECT查询都会重新获取一次Read View,而可重复读是第一SELECT操作才会生成Read View,之后的查询操作复用这一个。

    导致这两种的差距是因为:可重复读要保证一个事务中相同的SELECT读取的内容是相同的。

    MySQL MVVC多版本并发控制如何实现

    五、举例

    1.READ

    COMMITTED隔离级别下

    现在有两个事务id分别为10、20的事务在执行:

    -- id为10的事务
    begin;
    update t set name='李四' where id=1;
    update t set name='王五' where id=1;
    -- id为20的事务
    更新其他行的数据

    此刻,表中id为1的记录得到的版本链表如下所示:

    MySQL MVVC多版本并发控制如何实现

    此时新来一个事务执行如下操作:

    begin;
    select * from t where id=1;
    -- 事务10、20未提交

    查询到的结果为张三。

    具体的过程如下:

    • 在执行select语句前,先生成一个Read View,Read View的creator_trx_id为0,trx_ids列表的内容是[10,20],up_limit_id为10,low_limit_id为21。

    • 查询name为王五的最新版本的记录,按规则进行对比,因为trx_id为10,10刚好是trx_ids中的记录,所以这条记录对当前事务不可见,根据回滚指针得到下一个版本

    • 下一个版本name为李四,也不行

    • 继续找到name为张三的版本,trx_id为8,8小于up_limit_id,所以该版本对当前事务可见,得到最终结果

    接下来,再将id为10的事务进行commit提交。然后id为20的事务来更新记录:

    begin;
    -- id为20的事务
    update t set name='赵六' where id=1;
    update t set name='钱七' where id=1;

    此时版本链更新为:

    MySQL MVVC多版本并发控制如何实现

    再到刚才使用READ COMMITTED隔离级别的事务中继续查找这个id 为1的记录,得到的结果为name=王五的那条记录。执行过程如下:

    • 生成Read View,Read View的creator_trx_id为0,trx_ids列表的内容是[20],up_limit_id为20,low_limit_id为21。

    • 因为前两个版本的记录trx_id为20,存在trx_ids中,所以跳过

    • 到第三条记录时,trx_id为10,小于20,可以读取,所以最终结果为王五

    注意:READ COMMITTED,每次读取数据前都生成一个新的ReadView。

    2.REPEATABLE READ隔离级别下

    假如此时id为10的事务和id为20的事务正在修改,都未提交,修改内容和前面的一样,但是还未提交,此时当前事务做一个查询。

    MySQL MVVC多版本并发控制如何实现

    步骤为:

    • 生成Read View,Read View的creator_trx_id为0,trx_ids列表的内容是[10,20],up_limit_id为10,low_limit_id为21。

    • trx_id为10和20的都不满足要求

    • 最后查找到name为张三的历史版本的数据

    此时,id为10的记录提交事务。

    MySQL MVVC多版本并发控制如何实现

    当前事务又需要select id为1的记录,步骤为:

    • 因为是可重复读,且第一次select已经生成过Read View了,所有会复用它,不重新生成。

    • 所以trx_id为10和20的记录依旧不符合规则,最终得到的数据还是张三,符合可重复读的规范

    注意:REPEATABLE READ,每次读取都复用第一次生成的Read View

    3.如何解决幻读

    假设现在有一条数据,id为1

    MySQL MVVC多版本并发控制如何实现

    当前活跃的事务有10和20。

    此时当前事务启动了,执行如下SQL语句:

    begin;
    select * from student where id>=1;

    在开始前生成Read View,内容如下:creator_trx_id=0,trx_ids= [10,20] , up_limit_id=10, low_limit_id=21。

    由于id大于等于1的数据只有一个,且该数据的trx_id为8,小于up_limit_id,所以可以读取到。

    在这之后id为10的事务新增了一行数据,增加了id为2的数据,且提交了。

    MySQL MVVC多版本并发控制如何实现

    此时当前线程继续查找id>=1的数据,因为是可重复读,复用刚刚的Read View。

    得到两行数据,但是因为id为2的数据trx_id为10,该值在Read View的trx_ids中存在,所以该记录对当前事务不可见,所以最后查询到的数据只有一条记录。

    如果当前事务再插入id为2的数据就插不进去,所以说MVVC只解决了一半的幻读问题。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 MySQL
    相关文章 更多
    navicat导入数据库步骤详解及常见问题处理教程
    navicat导入数据库步骤详解及常见问题处理教程

    详解Navicat导入数据库的标准步骤,涵盖SQL脚本执行与CSV数据导入两种核心场景。针对导入过程中常见的字符集乱码、主键冲突及外键约束错误,提供具体的设置方法与修复建议,确保数据迁移准确无误。

    SQL主键约束和唯一约束有什么区别
    SQL主键约束和唯一约束有什么区别

    深入理解SQL PRIMARY KEY与UNIQUE约束的区别。文章从行标识与重复限制的目标出发,详解主键的非空特性、外键引用角色以及多数据库对NULL值的处理差异。结合users表实战示例,指导开发者如何根据字段稳定性与业务语义合理选择约束类型,避免设计误区。

    SQL约束有哪些类型及作用
    SQL约束有哪些类型及作用

    刚开始设计关系型数据库?了解PRIMARY KEY、FOREIGN KEY、UNIQUE、NOT NULL、CHECK、DEFAULT等约束的作用、语法和排错方法。本文以PostgreSQL语法为主,结合用户订单场景,清晰展示如何防止脏数据并处理常见约束冲突。

    SQL游标常见错误排查:死锁与资源未释放问题
    SQL游标常见错误排查:死锁与资源未释放问题

    深入解析SQL游标导致的死锁与资源未释放问题。通过PostgreSQL与MySQL语法对比,展示如何识别长事务持锁、分析锁等待链,并提供短事务提交、异常清理及批量处理的最佳实践,确保数据库连接与锁资源及时释放。

    SQL中怎么创建和删除约束
    SQL中怎么创建和删除约束

    后端开发者在维护数据库时,常需对现有表添加主键、唯一或外键约束。若直接执行ADD CONSTRAINT,往往因存量数据不合规导致报错。本文详解操作前的数据清洗步骤、标准SQL语法及MySQL等特殊写法,帮助你在生产环境安全完成结构变更。

    SQL分组统计查询:GROUP BY与HAVING子句用法示例
    SQL分组统计查询:GROUP BY与HAVING子句用法示例

    针对 SQL 初学者的分组统计实战指南。深入解析 GROUP BY 分组机制、WHERE 与 HAVING 的执行时机差异,以及 COUNT 函数对 NULL 值的处理逻辑。通过具体代码示例演示如何正确筛选聚合结果,并排查因 JOIN 连接导致的统计数据异常,提升报表准确性。

    SQL Server与MySQL游标基本语法及完整代码示例
    SQL Server与MySQL游标基本语法及完整代码示例

    本文详细对比SQL Server和MySQL中游标的基本用法。从DECLARE声明到FETCH遍历,再到CLOSE关闭与DEALLOCATE/Handler清理,解析两者在语法结构、变量接收及异常处理上的核心差异。通过标准代码示例展示如何安全使用游标,避免资源泄漏,适用于需要进行数据库存储过程开发或跨平台迁移的开发者。

    SQL多表连接区别:INNER JOIN与LEFT JOIN的使用场景
    SQL多表连接区别:INNER JOIN与LEFT JOIN的使用场景

    深入解析SQL中INNER JOIN与LEFT JOIN的核心区别。通过users和orders表实例,展示匹配行保留与NULL补位机制,重点讲解ON与WHERE子句中放置右表过滤条件的不同后果,提供防止LEFT JOIN退化的实战技巧及行数异常排查步骤。

    SQL CHECK约束的用法与取值范围限制
    SQL CHECK约束的用法与取值范围限制

    深入理解 SQL CHECK 约束在 PostgreSQL、MySQL 等数据库中的实现差异。文章通过订单业务场景,讲解如何定义单列范围与多列逻辑约束,分析 NULL 值对布尔条件的影响,并提供修改已有表结构时的安全操作建议,帮助开发者构建更健壮的数据完整性防线。

    SQL外键约束怎么用及级联操作说明
    SQL外键约束怎么用及级联操作说明

    后端开发中,如何正确使用 SQL 外键约束来维护订单与明细表的关系?本文通过 PostgreSQL 示例,深入讲解外键的创建语法、插入顺序要求,以及 ON DELETE CASCADE、RESTRICT、SET NULL 等策略的实际影响与风险控制,助你构建健壮的数据库结构。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。