前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL多版本并发控制原理-MVCC

MySQL多版本并发控制原理-MVCC

原创
作者头像
小草飞上天
发布2025-01-04 15:43:59
发布2025-01-04 15:43:59
1770
举报
文章被收录于专栏:java学习java学习

MVCC实现的目的?

MVCC多版本并发控制实现的主要目的,就是为了解决数据库的读与写的并发冲突,提高并发性能。

核心就是说。在读与写冲突的时候,尽最大可能不去加锁。

基于数据库的读与写实际上区分为几种:

第一:读与读

读与读实际并不会存在并发冲突,因为读并不涉及到数据库数据的变更。

第二:写与写

写与写实际也不会存在问题。因为写是一定会涉及数据库数据的变更的。所以当两个线程过来并发写的时候,那只能通过锁来解决,哪个线程拿到了锁,那就哪个线程去写。

第三:读与写

读与写是我们在数据库的操作中,需要考虑最多的情况了。因为在实际的业务中,基本都是读多写少的业务。所以在这种情况 下,我们就需要着重考虑如何来提高数据库的性能了。

为什么读与写我们不直接使用锁来处理。谁拿到了锁谁就来写,这里其实很好回答,就是为了性能。锁是影响整个数据库性能的最大问题之一。

MVCC的原理

简单的来说:MVCC就是将一条数据的所有历史版本都以链表的形式存储。不同的事务可以来访问整个链表中不同时期的数据版本快照。

这样的话。对于读来说可以去读取对应历史的版本,而写的话就可以写最新版本。互不冲突。

对于实现这样一个链表。mysql会基于每条数据来增加两个重要的字段

DB_TRX_ID: 创建或者修改数据的事务ID

这个字段记录的就是哪个事务来创建的这条数据。事务ID是系统分配的一个自增的数据,永远都从小到大。

DB_ROLL_PTR:回滚指针,指向记录的上一个版本。

这个字段记录的就是前一个事务的数据

例子

我们以一个实际的案例展示一下它的显示形式

下面这个图里面,每一行代表的都是这一条数据 的一个版本。

这个表里有2个字段 id 、name 。 另外两个显示的字段DB_TRX_ID DB_ROLL_PTR 是系统字段。

  1. 第一次事务,事务id : 100 进行修改name 为 mysql
  2. 第二个事务,事务id : 200 来修改name 为sql 时。数据库表中的数据被修改成 sql ,但事务200的DB_ROLL_PTR指针会指向事务100的修改记录,这样就形成了一个链路。
  3. 第三个事务,事务id : 300 来修改name 为mvvc .此时事务id300的修改就是最新数据。且DB_ROLL_PTR会指向事务200的数据。

整个的MVCC存储链路就是这样的一个原理。

但我们会想到。为什么数据库我看到的永远都是最新的。当然,这里的链路数据是存储在undo日志中的。

所以当事务进行回滚时,就可以通过这个链路找到对应的事务数据。

如何来通过链路找到对应的事务数据

我们来了解一下2个概念

当前读

当前读就是读取数据的最新版本。其他的事务不能修改记录。

例如:insert 、update 、delete 、select ... for update 等这些执行语句都是当前读,读取数据的最新版本。

快照读

快照读就是读取MVCC版本链路中的某一个版本,不需要加锁

例如:select 读取的就是当前事务中的快照数据。

read view读视图

读视图是一个很重要的概念,它才是如何找到对应版本的事务的数据核心

所谓读视图,实际上是记录并维护了系统当前事务的活跃事务id .哪些是活跃事务id ? (创建并未提交的事务)

我们看一下实际的案例如何来寻找对应版本数据:

下面的图中:有3个事务 100 、200 、300 ,活跃事务有 205 、255 、300 。也就是说只有300是未提交的。100和200 已经提交了。

  1. 当新事务301来查询数据时,首先读取了事务id=300的数据,也就是mvcc这条数据。但是事务301发现事务300是活跃事务(未提交事务),那事务300的值mvcc是不可见的。所以向下读取undo日志进行数据查找 。
  2. 通过事务id=300的这条数据的DB_ROLL_PTR指针在undo日志中找到了事务id=200这条数据
  3. 读取了事务id=200这条数据,发现事务id=200这条事务已经提交了,所以获取事务id=200这条数据。也就是值为:sql.

第二个例子:

下面的图中:有3个事务 100 、200 、300 ,活跃事务有 205 、255 、300 。也就是说只有300是未提交的。100和200 已经提交了。

  1. 当历史事务150来查询数据时,首先读取了事务id=300的数据,也就是mvcc这条数据。但是事务150发现事务300是活跃事务(未提交事务),那事务300的值mvcc是不可见的。所以向下读取undo日志进行数据查找 。
  2. 通过事务id=300的这条数据的DB_ROLL_PTR指针在undo日志中找到了事务id=200这条数据
  3. 读取了事务id=200这条数据,发现事务id=200这条事务已经提交了,但是事务200大于事务150.也就是说事务200是在150后面产生的事务。那对于事务150来说,也是不可见的。所以继续向下读取undo日志进行数据查找
  4. 通过事务id=200的这条数据的DB_ROLL_PTR指针在undo日志中找到了事务id=100这条数据
  5. 与上面的流程一样。找到事务id100且已提交,于是读取数据为 : mysql

当然了。如果是事务300来读取数据,那返回 的数据肯定 是其本身的数据了。也就是 mvcc

总结

总体来说,MVCC是一种强大的多版本并发控制,能解决mysql读写冲突中的并发效率问题。尽可能少的去加锁,来提高并发效率。

本文简明扼要的介绍了MVCC的目的及实现原理并通过实际的案例来说明了如何去查找快照。很好的理解MVCC的版本控制是如何做到的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 [email protected] 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 [email protected] 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MVCC实现的目的?
    • 第一:读与读
    • 第二:写与写
    • 第三:读与写
  • MVCC的原理
    • 例子
  • 如何来通过链路找到对应的事务数据
    • 当前读
    • 快照读
    • read view读视图
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档