MVCC

MVCC允许多个事务同时读取同一行数据,而不会彼此阻塞,每个事务看到的数据版本是该事务开始时的数据版本。这意味着,如果其他事务在此期间修改了数据,正在运行的事务仍然看到的是它开始时的数据状态,从而实现了非阻塞读操作。
多版本并发控制,存在于读已提交与可重复读两个隔离级别下。读未提交下直接读最新的数据,不需要MVCC,串行化普通查询都给你上表锁,也没有MVCC一说。

实现原理

事务隔离级别的无锁的实现方式

首先:InnoDB为每行数据维护一个版本链。每次对数据进行修改时,都会生成一个新的版本,而不是直接覆盖旧数据。每个版本包含两个关键信息:

- 事务ID:标识创建该版本的事务。

- 回滚指针:指向旧版本的数据,形成一个版本链。

然后,当一个事务开始时,InnoDB会为它生成一个Read View,用于记录当前系统中所有活跃事务的ID。Read View的作用是帮助事务判断哪些数据版本对它可见。

MVCC 是通过 ReadView+ 版本链 实现的。

当事务读取数据时,InnoDB会根据Read View和版本链中的事务ID,判断哪个版本对当前事务是可见的。具体规则如下:

- 如果数据版本的事务ID小于当前事务ID,并且不在活跃事务列表中,则该版本可见。

- 如果数据版本的事务ID大于当前事务ID,或者属于活跃事务,则该版本不可见,需要沿着版本链继续查找更旧的版本。

通过这种方式,MVCC实现了非阻塞的读操作,即读操作不会阻塞写操作,写操作也不会阻塞读操作。


readview字段:

这样,一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:

如果记录的 事务 id 值小于 Read View 中的 min 事务 id 值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见。

如果记录的 事务 id 值大于等于 Read View 中的 max 事务 id 值,表示这个版本的记录是在创建 ReadView 后才启动的事务生成的,所以该版本的记录对当前事务不可见。

如果记录的 事务 id 值在 Read View 的 min trx id 和 max trx id 之间,需要判断 trx id 是否在 事务id 列表中:

如果记录的 trx id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。

如果记录的 trx id 不在 m ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。


MVCC在不同隔离级别下的表现

  • 读未提交:MVCC 被最少使用,甚至在 InnoDB 中无法完全支持该隔离级别,因为撤销了脏读的实现。

  • [读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。

  • [可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。确保所有读取操作基于同一个快照,避免了不可重复读和脏读。

  • 串行化:通过强制事务串行执行,完全消除并发问题。MVCC 在串行化级别下与可重复读类似,但会引入更多的锁,从而保证事务的串行性。


为什么需要MVCC

MVCC的出现是为了解决传统锁机制在高并发场景下的局限性。

以下是MVCC的几个核心优势:

  1. 提高并发性能

在传统的锁机制中,读写操作是互斥的。如果一个事务正在读取数据,另一个事务想要修改这行数据,就必须等待。这种锁机制会导致大量的阻塞和等待,降低系统的并发性能。MVCC通过多版本控制,允许读操作和写操作同时进行。读操作读取的是历史版本,而写操作生成新版本,两者互不干扰,从而大大提高了系统的并发性能。

  1. 避免锁冲突

锁机制在高并发场景下容易引发死锁问题。例如,事务A锁定了行1,事务B锁定了行2,然后事务A尝试锁定行2,事务B尝试锁定行1,这时就会发生死锁。MVCC通过版本控制减少了锁的使用,降低了锁冲突的概率,从而避免了死锁问题

  1. 实现一致性读取

MVCC确保事务读取数据时看到的是事务开始时的快照,而不是最新的数据。这种机制保证了事务的隔离性,避免了以下问题:

脏读 :读取到未提交的数据

不可重复读:同一事务中多次读取同一数据,结果不一致。

幻读:同一事务中多次查询,结果集不一致。

通过MVCC,MySQL可以在保证数据一致性的同时,提供更高的并发性能。

  1. 支持长事务

在一些场景中,事务可能需要运行很长时间(例如数据分析)。如果使用传统的锁机制,长事务会长时间占用锁资源,导致其他事务无法执行。MVCC通过多版本控制,允许长事务读取历史版本的数据,而不会影响其他事务的写操作。


posted @ 2025-03-16 09:44  Zero&&One  阅读(67)  评论(0)    收藏  举报