MySQL 多版本并发控制 MVCC

MVCC出现背景

事务的4个隔离级别以及对应的三种异常

读未提交(Read uncommitted

读已提交(Read committed):脏读

可重复读(Repeatable read):不可重复读

串行化(Serializable):幻读

  • 脏读:一个事务读取到了另外一个事务没有提交的数据;
  • 不可重复读:在同一个事务中,两次读取同一数据,得到内容不同;
  • 幻读:同一事务中,用同样的操作读取两次,得到的记录数不同。

MySQL中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题,但不能解决幻读的问题。如果我们需要解决幻读的问题,就需要采用串行化的方式,也就是将隔离级别提升到最高,但这样依赖就会大幅降低数据库的事务并发能力。

MVCC就是通过乐观锁的方式来解决不可重复读和幻读的问题,它可以在大多数情况下替代行级锁,降低系统的开销。

MySQL并发事务会引起更新丢失问题,解决办法是锁,主要分两类:

  • 乐观锁

​ 其实就如它的名字一样,非常乐观,总是假设比较好的情况。

​ 每次取数据的时候都认为他人不会对其修改,所以不会上锁,但是会在更新的时候进行判断,看看在此期间内有没有人去更新这个数 据,可以使用版本号机制和CAS算法实现。

  • 悲观锁

​ 与乐观锁完全相反,非常悲观,总是假设非常糟糕的情况。

​ 每次取数据的时候都认为他人会对其进行修改,所以每次拿数据的时候都会加上锁,这样别人想拿数据的话就会阻塞,除非它拿到 锁。

什么是MVCC

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

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

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

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

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

MVCC解决了什么问题

解决了在REPEATABLE READ和READ COMMITTED两个隔离级别下读同一行和写同一行的两个事务的并发。

  1. 读写直接阻塞的问题:通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。

    提高并发的演进思路:

    • 普通锁,只能串行执行;
    • 读写锁,可以实现读读并发;
    • 数据多版本并发控制,可以实现读写并发。
  2. 降低了死锁的概率:因为 InnoDBMVCC采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。

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

举个简单的例子:

  1. 一个事务AtxnId=100),修改了数据X,使得X=1,并且commit了。
  2. 另外一个事务BtxnId=101)开始尝试读取X,但是还X=1。但B没有提交。
  3. 第三个事务CtxnId=102)修改了数据X,使得X=2,并且提交了。
  4. 事务B又一次读取了X。这时
    • 如果事务BRead Committed,那么就读取X的最新commit的版本,也就是X=2。
    • 如果事务B是Repeatable Read。那么读取的就是当前事务(txnId=101)之前X的最新版本,也就是XtxnId=100提交的版本,即X=1。

注意,这里B不论是Read Committed,还是Repeatable Read,都不会被锁,都能立刻拿到结果。这也就是MVCC存在的意义。

快照读与当前读

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

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

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

select * from users where id = '1';

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

select * from users where id = '1' lock in share mode;
select * from users where id = '1' for update;

MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读,而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现

InnoDB的MVCC是如何工作的

当查询一条记录的时候,执行流程如下:

  1. 首先获取事务自己的版本号,也就是事务 ID
  2. 获取 Read View
  3. 查询得到的数据,然后与 Read View 中的事务版本号进行比较;
  4. 如果不符合 Read View 规则,就需要从 Undo Log 中获取历史快照;
  5. 最后返回符合规则的数据。

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

事务版本号

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

行记录的隐藏列

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

  • DB_ROW_ID6-byte,隐藏的行ID,用来生成默认聚簇索引。如果我们创建数据表的时候没有指定聚簇索引,这时InnoDB就会用这个隐藏ID来创建聚簇索引。采用聚簇索引的方式可以提升数据的查找效率。
  • DR_TRX_ID6byte,操作这个数据的事务ID,也就是最后一个对该数据进行插入或更新的事务ID
  • DB_ROLL_PTR7byte,回滚指针,也就是指向这个记录的Undo log信息。

InnoDB数据记录隐藏列

Undo Log

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

Undo Log回滚历史记录

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

Read View(读视图)

什么是Read View,说白了Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

所以我们知道Read View主要是用来做可见性判断的,即当我们某个事务只想快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前事务是否能够看到哪个版本的数据,即可能是当前最新的数据,也有可能是该行记录的undo log里面的的某个版本数据。

  • trx_list 未提交事务ID列表,用来维护Read View生成时刻系统正活跃的事务ID
  • up_limit_id 记录trx_list列表中事务ID最小的ID
  • low_limit_id ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1
  • 首先比较DB_TRX_ID < up_limit_id, 如果小于,则当前事务能看到DB_TRX_ID 所在的记录,如果大于等于进入下一个判断
  • 接下来判断 DB_TRX_ID 大于等于 low_limit_id, 如果大于等于则代表DB_TRX_ID 所在的记录在Read View生成后才出现的,那对当前事务肯定不可见,如果小于则进入下一个判断
  • 判断DB_TRX_ID 是否在活跃事务之中,trx_list.contains(DB_TRX_ID),如果在,则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的;如果不在,则说明,你这个事务在Read View生成之前就已经Commit了,你修改的结果,我当前事务是能看见的

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

查询

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

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

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

删除

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

更新

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

参考文章

MySQL - MySQL InnoDB的MVCC实现机制

MySQL的多版本并发控制(MVCC)是什么?

值得收藏,揭秘 MySQL 多版本并发控制实现原理

MVCC解决了什么问题?

MVCC实现原理之ReadView(一步到位)

MVCC百度百科

脏读、不可重复读的最终解决方案——MVCC

RR有幻读问题吗?MVCC能否解决幻读?

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Yield Guild Games 宣布与区块链游戏中心 Iskra 建立战略合作伙伴关系

Yield Guild Games (YGG) 宣布将向 Iskra 引入其任务系统&#xff0c;Iskra 是一个 Web3 游戏中心和发布平台&#xff0c;拥有超过 400 万注册钱包和 10 万月度活跃用户 (MAU)。在 LINE、Kakao、Wemade 和 Netmarble 等公司的支持下&#xff0c;Iskra 将游戏玩家和游戏工作室聚…

人工智能-机器学习-深度学习-分类与算法梳理

人工智能-机器学习-深度学习-分类与算法梳理 目前人工智能的概念层出不穷&#xff0c;容易搞混&#xff0c;理清脉络&#xff0c;有益新知识入脑。 为便于梳理&#xff0c;本文只有提纲&#xff0c;且笔者准备仓促&#xff0c;敬请勘误&#xff0c;不甚感激。 请看右边目录索引…

(六)深入理解Bluez协议栈之“GATT Client Profile”

前言: 本章节我们继续介绍GATT Client Profile的实现,参考的程序是tools\btgatt-client.c,需要注意的一点,在./configure时,需要添加 --enable-test --enable-testing才会编译该c文件,编译完成后,生成的可执行程序为btgatt-client。本文主要以btgatt-client运行时可能会…

01.CheckStyle代码检查工具

CheckStyle代码检查工具 1.介绍 Checkstyle 是一种开发工具&#xff0c;可帮助程序员编写符合编码标准的 Java 代码。它使检查 Java 代码的过程自动化&#xff0c;从而使开发者免于完成这项无聊&#xff08;但重要&#xff09;的任务。这使得它非常适合想要强制执行编码标准的…

synchronized的介绍

1.synchronized的介绍和作用 synchronized是Java编程语言中的一个关键字&#xff0c;用于实现线程同步。在多线程编程中&#xff0c;多个线程可能同时访问共享资源&#xff0c;而这可能导致数据不一致或其他问题。为了避免这些问题&#xff0c;可以使用 synchronized 关键字来…

华尔街日报:中国加密货币交易“非法却盛行”,VPN翻墙、微信找币商、线下面交……

《华尔街日报》戏谑地称&#xff0c;中国的投资者曾经是加密货币交易的主导力量&#xff0c;人民币是用于交易比特币最受欢迎的法定货币。而现在&#xff0c;中国的币圈投资者正努力规避政府对加密货币交易的严格规定。 事实上&#xff0c;在过去几年里&#xff0c;中国大陆与加…

无需编程,简单易上手的家具小程序搭建方法分享

想要开设一家家具店的小程序吗&#xff1f;现在&#xff0c;我将为大家介绍如何使用乔拓云平台搭建一个家具小程序&#xff0c;帮助您方便快捷地开展线上家具销售业务。 第一步&#xff0c;登录乔拓云平台进入商城后台管理页面。 第二步&#xff0c;在乔拓云平台的后台管理页面…

自动化工具 基于 Antd+DRF 开发了一款适配 JMeter 的接口自动化测试报告

JMeter Report 基于 AntdDRF 开发的一款 JMeter 测试报告服务&#xff0c;用于在 JMeter 接口测试中使用。 &#x1f334; 背景 JMeter 是测试工作中常用的一款工具&#xff0c;除了压测还可以用来做接口自动化的测试。 从事测试多年&#xff0c;接口自动化也做过很多的尝试…

链表存数相加算法(leetcode第2题)

题目描述&#xff1a; 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外&#xff0c;这…

WebSocket协议、与HTTP对比

WebSocket 也可前往本人的个人网站进行阅读 WebSocket 和 HTTP WebSocket和HTTP协议一样&#xff0c;都是基于TCP协议实现的应用层协议。 HTTP协议通常是单边通信&#xff0c;主要用于传输静态文档、请求-响应通信&#xff0c;适用于Web浏览器加载网页、API调用等。然而Web…

Message queue 消息队列--RabbitMQ 【基础入门】

一&#xff0c;Message queue介绍&#xff1a; 1.1使用消息队列的优点&#xff1a; 服务之间最常见的通信方式是直接调用彼此来通信,消息从一端发出后立即就可以达到另一端,称为即时消息通讯(同步通信) 消息从某一端发出后,首先进入一个容器进行临时存储,当达到某种条件后,再由…