MVCC(Multi-Version Concurrency Control,多版本并发控制)

2024 年 9 月 22 日 星期日(已编辑)
2
摘要
MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库系统中的一种并发控制机制,通过为每个事务提供数据的不同版本,允许多个事务并发地读取和写入数据库,而不会互相阻塞。MVCC的核心思想是为每个事务提供数据的不同版本,事务在读取数据时可以选择读取到某个特定的版本,而不是直接读取当前最新的数据版本。MVCC通过保存数据的多个版本来管理并发,使用Undo Log和事务版本控制实现。事务在读取数据时可以选择快照读或当前读,写入时生成新版本并保存旧版本到Undo Log。MVCC的优势包括高并发、读取历史版本、减少锁竞争,但也存在版本管理开销、空间消耗、清理旧版本的成本等不足之处。总体而言,MVCC通过管理多个数据版本来处理并发问题,提升了系统并发性能和数据隔离性。
这篇文章上次修改于 2024 年 9 月 22 日 星期日,可能部分内容已经不适用,如有疑问可询问作者。

MVCC(Multi-Version Concurrency Control,多版本并发控制)

MVCC(Multi-Version Concurrency Control,多版本并发控制) 是数据库系统中的一种并发控制机制,用来处理多个事务同时操作数据库时可能产生的冲突问题。通过 MVCC,数据库在处理并发事务时,可以在提供高并发性的同时保证事务的隔离性(特别是在 MySQL 的 InnoDB 存储引擎中,默认的隔离级别是 可重复读)。

MVCC 的核心思想

MVCC 的核心思想是:为每个事务提供数据的不同版本,允许多个事务并发地读取和写入数据库,而不会互相阻塞。当一个事务在读取数据时,它不会立即看到其他事务正在写入的数据,而是读取数据的历史版本,这样避免了直接的锁冲突。

基本原理

MVCC 通过保存数据的多个版本来管理并发,事务在读取数据时可以选择读取到某个特定的版本,而不是直接读取当前最新的数据版本。InnoDB 使用了 Undo Log(回滚日志) 和事务版本控制来实现 MVCC。这里是 MVCC 的几个关键要素:

  1. 数据的隐藏版本(快照)

    • 每条记录在数据库中都保存着多个版本的历史快照。
    • 每个版本的数据都带有两个隐藏的时间戳字段:trx_idroll_pointer
      • trx_id:记录是哪一个事务修改的该数据。
      • roll_pointer:指向 Undo Log,即这个版本的前一个版本。
  2. Undo Log(回滚日志)

    • 当一个事务修改数据时,InnoDB 会把修改前的旧数据保存到 Undo Log 中,这样可以实现数据回滚或提供历史版本数据给其他事务读取。
    • Undo Log 会将旧的版本链起来,形成一个数据的版本链,每个事务可以根据自己的时间戳选择合适的版本。
  3. 事务的快照版本

    • 每个事务在开始时都会记录一个当前数据库系统的“快照”(系统中各条记录的状态)。
    • 在事务的生命周期内,即使其他事务修改了数据,当前事务依然会读取到启动时看到的快照数据,而不会受到其他事务的影响。
  4. 事务时间戳

    • 每个事务都有一个唯一的时间戳,通常由事务启动的时间或者事务 ID 标识。这个时间戳帮助事务确定它可以看到的数据版本。

MVCC 读取与写入机制

1. 读取(读操作)

当一个事务读取数据时,MVCC 会选择一个特定版本的记录给这个事务,而不是直接返回最新的版本。这有两种主要的读取模式:

  • 快照读(Snapshot Read):读取的是符合当前事务可见性的历史版本数据,事务并不会被其他事务阻塞。例如 SELECT 操作。

    • 对于可重复读隔离级别,事务在第一次读取数据时会创建一个快照,后续的读取操作都会基于这个快照,即使其他事务修改了数据,当前事务也不会感知到。
  • 当前读(Current Read):直接读取最新版本的数据,并加锁以防止其他事务修改,例如 SELECT ... FOR UPDATEUPDATEDELETE。当前读确保事务可以读到最新的数据,并且保证后续的操作都是基于最新的数据进行的。

2. 写入(写操作)

写入操作时,事务会:

  • 生成一个新的版本,包含修改后的数据。
  • 将修改前的旧版本写入 Undo Log,以便其他事务能够继续访问这个旧版本的数据。
  • 为新版本数据打上当前事务的时间戳(trx_id),这样其他事务就知道哪个事务修改了这条数据。

在写入时,写操作的事务不会直接覆盖旧的数据,而是创建一个新的版本,因此不会阻塞正在进行的读操作。旧版本的数据依然可以被其他未提交的事务读取。

MVCC 的事务可见性规则

事务是否可以看到一条记录的某个版本取决于以下几条规则:

  1. 版本的 trx_id 小于当前事务的 ID:即当前事务开始之前,数据的这个版本已经提交了,因此这个版本是可见的。

  2. 版本的 trx_id 大于当前事务的 ID:表示该版本是在当前事务启动之后由其他事务生成的,因此这个版本对当前事务是不可见的。

  3. 删除的版本不可见:如果某个版本的数据在当前事务之前已经被其他事务删除,则当前事务也看不到这个被删除的版本。

举个简单的例子来解释 MVCC 的运作

假设有两个事务 A 和 B,它们都要对数据库中的某条记录进行操作。

  • 事务 Asql START TRANSACTION; UPDATE orders SET amount = 500 WHERE id = 1;

    事务 A 更新了订单 ID 为 1 的记录,将其金额修改为 500。但事务 A 并没有立即提交,这时另一个事务 B 开始读取数据。

  • 事务 Bsql START TRANSACTION; SELECT amount FROM orders WHERE id = 1;

    根据 MVCC 机制,事务 B 看到的不是事务 A 修改后的数据(500),而是事务 A 修改前的旧数据(假设修改前是 400)。这就是 MVCC 通过读取数据的旧版本,避免了事务 B 被事务 A 的操作所阻塞。

  • 事务 A 提交sql COMMIT;

    当事务 A 提交后,事务 B 在它的事务结束前依然会看到旧的 400 数据,只有当事务 B 结束并重新开始新的事务时,才会读取到最新的 500 数据。

MVCC 优势与不足

优势:

  1. 高并发:MVCC 允许事务并发执行,读写操作不会互相阻塞,提升了系统的吞吐量。
  2. 读取历史版本:事务在读取数据时,能读取到符合自己事务版本的历史快照,确保事务的隔离性。
  3. 减少锁竞争:MVCC 在大部分情况下不需要加锁,避免了读写之间的直接冲突。

不足:

  1. 版本管理开销:MVCC 需要管理和维护多个数据版本,尤其是在长事务和大数据量场景下,回滚日志会增加存储和性能开销。
  2. 空间消耗:保存多个版本的历史数据需要额外的存储空间,Undo Log 也会占用存储资源。
  3. 清理旧版本的成本:数据库需要定期清理无用的旧版本数据,否则可能导致性能下降。

总结

MVCC 是 MySQL InnoDB 存储引擎用来处理并发问题的重要机制,它通过为每条数据维护多个版本,避免了读写之间的锁定冲突,提升了并发性能。事务在读取数据时读取的是符合其版本的快照,确保了隔离性,并且允许多个事务并发读写数据,保持数据一致性。

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...