mysql mvcc机制详解

什么是当前读、快照读?

当前读

当前读指的是读取数据的​**​最新提交版本。**底层是通过对数据加锁加锁实现的

当前读sql语句有:

  • select lock in share mode
  • select for update
  • insert
  • update
  • delete

快照读

快照读指的是读取数据在某个时间点的​​快照(版本)​​,而不是数据的最新版本

快照读sql语句:普通selcet语句。底层是利用mvcc机制实现的


MVCC是什么?

mvcc就是多版本并发控制。多版本:指mysql维护着行数据的多个版本。

并发控制 :在多个事务同时操作某一行记录时,mysql控制返回多个版本的行记录中的某个版本

用来解决什么问题?

用来解决读写冲突问题。

数据库(普通select语句)并发事务时会出现脏读、不可重复读、幻读问题,也需要利用mvcc机制来解决。

流程:一条select语句来查询数据,但是查询的数据有多个版本,这时就要选择其中的某一个版本返回

数据隔离级别和并发问题

脏读 (Dirty Read)、不可重复读 (Non-Repeatable Read) 和 幻读 (Phantom Read)​​ 是数据库事务并发执行时,在没有采取足够隔离措施的情况下,会产生的三类典型问题。

mvcc机制实现了隔离级别,可以解决这三类并发问题

脏读

要解决脏读,就得上读已提交的隔离级别

得保证一个事务只能读取到其他事务已经提交的数据,没提交的就不该读取到

不可重复读

解决不可重复读问题,就得上读已提交隔离级别

一个事务第一次读取之后,后面读取得每一次数据都应该和第一次读取的一样

那就得维护一份数据的多个版本

每个事务修改一次就生成一个新版本,让不同事务读取不同的版本

在读已提交的得隔离级别下,让事务去读已经提交的数据版本,就可以避免脏读

在不可重复读的隔离级别下,让事务每次读到同一个版本的数据,这样每次读到的都是一样的了,就避免的不可重复读的问题

把修改的数据版本记录到undo log中,给表加一个隐藏字段,叫回滚指针

回滚指针指向undolog日志,日志记录了该表的修改记录的各个版本

利用回滚指针将历史版本串联成一个列表,想读哪个,查找即可

那一个事务来查数据,怎么知道要查哪个版本的数据呢?

所以要为每个事务分配一个id,事务id自增分配。通过对比事务id的大小,就知道哪个事务创建的早、哪个晚,记录哪些事务提交了,哪些未提交,让提交的早的事务不要看提交的比较晚的数据即可。所以要给表加上隐藏字段修改数据的事务id,谁修改了这条数据,就把对应的事务id记录下来

readview:4个重要字段:比较复杂,利用数轴来看

记住作用:就是决定在多个版本中,到底该读哪一个版本

undolog记录数据版本

readview去判断这个数据版本对当前事务的可见性

MVCC怎么实现的?

隐藏字段、undolog版本链、readview

undolog版本链、readview是怎么回事?

四种隔离级别怎么实现的

读未提交

不加任何读锁实现的。(假设:我现在在读这条数据,加了读锁,别人就不能修改。不加读锁,别人就可以修改我在读的数据)

读操作不加锁,就可以直接读取最新版本的数据,即使这些数据被其他事务用排他锁锁住(脏读)

会发生:脏读、不可重复读、幻读

读已提交

可以基于锁实现也可以基于mvcc实现(mvcc更常见)

mvcc的实现原理:

每个​​语句​​开始时都会生成一个新的快照(读视图readview)。

一个 Read View主要包含以下核心内容:

  1. m_ids: 一个列表,记录了在本 Read View创建时,系统中所有​​活跃的(尚未提交的)事务​​的事务 ID
  2. min_trx_id: 记录 m_ids列表中最小的那个事务 ID。
  3. max_trx_id: 记录在本 Read View创建时,系统已经分配的下一个事务 ID(即当前最大事务 ID + 1)。
  4. creator_trx_id: 创建这个 Read View的事务自己的 ID(只有该事务自己有写操作时,这个值才不为空)。

每次查询看到的都是这个​​语句级快照​​的最新已提交数据。所以在一个事务内,两次相同的查询可能会看到不同的数据(不可重复读,在'读已提交'隔离级别下,两次查询分别生成了两个不同的 Read View)。

判断某一事务对于当前创建快照的事务是否可见的规则

可重复读

mvcc的实现原理:

在​​事务开始​​时生成一个一致性快照,而不是语句开始。

整个事务期间,所有​​普通​​的 SELECT查询都基于这个​​事务级快照​​。因此,无论查询多少次,看到的数据都是一致的。这样就不会生成多个快照。

串行化

读未提交怎么实现?

不用管

串行化:加锁

可重复读怎么实现?(复用readview和mvcc机制)

可重复读会在第一次select时生成一个readview,通过readview判断应该读哪个数据

读已提交怎么实现?

每次select时就会生成一个新的readview

相关推荐
程序新视界5 小时前
MySQL中什么是回表查询,如何避免和优化?
mysql
薛定谔的算法9 小时前
phoneGPT:构建专业领域的检索增强型智能问答系统
前端·数据库·后端
Databend11 小时前
Databend 亮相 RustChinaConf 2025,分享基于 Rust 构建商业化数仓平台的探索
数据库
得物技术12 小时前
破解gh-ost变更导致MySQL表膨胀之谜|得物技术
数据库·后端·mysql
Java水解12 小时前
【MySQL】从零开始学习MySQL:基础与安装指南
后端·mysql
Raymond运维16 小时前
MariaDB源码编译安装(二)
运维·数据库·mariadb
沢田纲吉16 小时前
🗄️ MySQL 表操作全面指南
数据库·后端·mysql
RestCloud1 天前
SQL Server到Hive:批处理ETL性能提升30%的实战经验
数据库·api
RestCloud1 天前
为什么说零代码 ETL 是未来趋势?
数据库·api
ClouGence1 天前
CloudCanal + Paimon + SelectDB 从 0 到 1 构建实时湖仓
数据库