【MySQL数据库 | 第二十五篇】深入探讨MVCC底层原理
前言:
在当今互联网时代,数据库扮演着数据存储和管理的关键角色。对于大型Web应用程序和企业级系统而言,高效地处理并发访问和事务管理是至关重要的。多版本并发控制(MVCC)是一种数据库事务处理的技术,旨在提高并发性和数据一致性,而MySQL是其中一个广泛采用MVCC的数据库管理系统。
在本文中,我们将深入探讨MVCC的概念、原理和实现方式,特别关注MySQL中MVCC的实现。我们将探讨MVCC是如何克服传统数据库锁定机制的局限性,从而实现更高的并发性和更好的数据一致性。通过深入了解MVCC,读者将能够更好地理解MySQL的工作原理,并能够优化数据库设计和性能调优。
目录
前言:
当前读:
快照读:
MVCC:
实现原理:
RC隔离级别下:
RR隔离级别下:
总结:
本片的数据库表的属性,如果没有特殊说明,那默认就是(innoDB+RR)
在介绍MVCC之前,我们要先介绍两个概念:
当前读:
读取的是当前记录的最新版本,读取的时候还需要保证其他并发事务不能修改当前记录,会对当前记录加锁。对于我们日常的select......lock in share mode,select......for update,update,insert,delete都是一种当前读。
快照读:
读取的是数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。常见隔离级别下的select:
- RC:每一次select,都生成一个快照读。
- RR:开启一个事务之后,只有第一个select语句才会生成一张快照,此后读的都是快照中的数据,直到事务提交。
- Serializable:快照读退化为当前读。
我们用一个例子来看一下,以下为我们模拟的表数据(数据库默认使用InnoDB,隔离级别为RR):
我们同时开启两个MySQL客户端来对这张表进行操作:
先尝试读取数据,这种能读取到表中最新数据的模式就叫做当前读。现在我们来看看什么是快照读:
1.创建两个MySQL客户端,都开启事务,并且在第一个MySQL客户端中做一次SQL查询。
2.在第二个MySQL客户端中对id为1的用户姓名进行修改,并且提交当前事务。
3.重新在第一个MySQL客户端中查询。
我们会发现:第一个MySQL客户端中读取的数据竟然是老数据。这是为什么呢?
原因很简单:之前我们讲过MySQL的innoDB引擎在RR的隔离级别下,当我们开启事务的时候,只有第一次Select是当前读,读取完之后会生成一张快照,此后这个事务中后续的所有相同的select语句读的都是第一次Select所生成的快照。
当我们提交第一个MySQL客户端的时候之后,再尝试select * from emp ,会发生什么呢?
我们可以发现,此时就可以读取到正常的数据了。也就是说在InnoDB中select....... in share mode语句就是当前读。
MVCC:
MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种用于数据库管理系统中实现并发控制的技术。它允许多个事务同时对数据库进行读写操作而不会导致数据不一致或丢失。
MVCC 的核心思想是在数据库中维护多个数据版本,并根据事务的隔离级别来决定哪个版本对特定事务是可见的。快照读就为MVCC提供了一个非阻塞读的功能,MVCC的具体实现还需要依赖数据库中的三个隐式字段,undo log 日志,readView
实现原理:
1.记录中的隐藏字段:当我们尝试创建一张表之后,InnoDB会自动为我们加上两个字段:
- DB_TRX_ID:最近一次修改或删除记录的事务ID
- DB_ROLL_PTR:回滚指针,指向记录的上一个版本
- DB_ROW_ID:隐藏主键,如果表结构没有主键,将会生成该隐藏字段
2.undo log 日志:
- Undo 日志记录了对数据的修改操作,包括插入、更新、删除等。
- Undo 日志中记录了修改前的数据值,以及撤销操作所需的信息,以便在事务回滚或 MVCC 中使用。
- 当事务提交的时候,相关的Undo log 日志就被标记为可回收状态,可以在之后的操作中被回收
如果是insert语句,那么Undo log日志只需要在回滚的时候需要,当事务提交之后,会被立即删除。
如果是update,delete的时候,产生的undo log日志不仅在回滚的时候需要,在产生快照读的时候也需要,因此其不会被立即删除。(undo log 中残留的旧版本数据可供其他并发事务进行快照读)
3.undo log 版本链:
(本图来自黑马程序员,偷懒了没画,如有侵权,请联系我立即删除)
4.readview
readview是快照读SQL执行时候MVCC提取数据的依据,记录并且维护当前事务活跃的事务(未提交)的id。
ReadView包含了四个核心字段:
字段 含义 m_min_trx_id 最小活跃事务ID max_try_id 预分配事务ID,其实是当前最大事务ID+1(因为事务是自增的) m_ids 当前活跃的事务ID集合 creator_trx_id ReadView创建者的事务ID 那么基于readView,其实就已经决定了哪些事务可以访问undo的哪些数据版本:
InnoDB不同的隔离级别,生成ReadView的实际不同:










