💕💕 推荐:体系化学习Java(Java面试专题)
文章目录
- 1、什么是 MVCC
- 2、什么是当前读、快照读
- 3、MVCC 具体解决什么问题
- 4、MVCC 的实现原理
- 4.1、4个隐式字段
- 4.2、undo 日志
- 4.3、Read View
- 5、使用 MVCC 时,需要注意什么问题
1、什么是 MVCC
MVCC(Multi-Version Concurrency Control)是一种多版本并发控制技术,常用于数据库管理系统中,用于支持事务的并发执行。MVCC 技术可以在读取数据时不产生锁,同时保证数据的一致性。具体来说,MVCC 技术会在每个数据行上保存多个版本的数据,每个版本都有一个时间戳,当一个事务需要读取数据时,会根据该事务的时间戳选择合适的数据版本进行读取,从而避免了读取数据时的锁定操作。同时,MVCC 技术还可以通过回滚日志和垃圾回收机制来保证数据的一致性和完整性。MVCC 技术在 InnoDB 存储引擎中得到了广泛的应用,成为了 InnoDB 存储引擎的一个重要特性。
正是因为有了 MVCC,所以 Mysql 的并发性能才会更好。
2、什么是当前读、快照读
当前读和快照读是 InnoDB 存储引擎中的两种读取数据的方式。
当前读:是指在读取数据时,使用的是最新的数据版本。当前读可以通过 SELECT … FOR UPDATE (排它锁)或者 SELECT … LOCK IN SHARE MODE (共享锁)等语句实现。在当前读的情况下,如果其他事务正在修改该数据行,当前读会被阻塞,直到其他事务释放锁。
快照读:是指在读取数据时,使用的是指定时间点的数据版本。快照读可以通过 SELECT … FROM … AS OF 或者 SELECT … FROM … VERSIONS BETWEEN … AND … 等语句实现。在快照读的情况下,如果其他事务正在修改该数据行,快照读不会被阻塞,而是读取该数据行的历史版本。快照读可以提高并发读取数据的效率,但是可能会读取到已经被修改的数据行。
SELECT ... FROM ... AS OF
是一种快照读取数据的方式。它可以在读取数据时指定一个时间点,然后读取该时间点的数据版本,而不是当前的数据版本。这种方式不会对其他事务产生锁定,因此可以提高并发性能。在使用AS OF
语句时,InnoDB 存储引擎会从回滚日志中读取指定时间点的数据版本,并返回给用户。需要注意的是,使用AS OF
语句时,需要保证指定的时间点在当前事务开始之前,否则可能会读取到未提交的数据,导致数据不一致。
SELECT ... FROM ... VERSIONS BETWEEN ... AND ...
是一种快照读取数据的方式,可以读取指定时间范围内的数据版本。在使用该语句时,需要指定开始时间和结束时间,然后 InnoDB 存储引擎会读取这个时间范围内的所有数据版本,并将它们返回给用户。这种方式不会对其他事务产生锁定,因此可以提高并发性能。需要注意的是,使用VERSIONS BETWEEN
语句时,需要保证指定的时间范围在当前事务开始之前,否则可能会读取到未提交的数据,导致数据不一致。
当前读适合于需要修改数据的场景,而快照读适合于只需要读取数据的场景。
3、MVCC 具体解决什么问题
首先说下数据库的并发场景可以分为以下几种:
-
读写并发:多个事务同时对同一数据进行读和写操作。这种场景需要使用并发控制机制来保证数据的一致性和完整性。
-
写写并发:多个事务同时对同一数据进行写操作。这种场景需要使用锁定机制来保证数据的一致性和完整性。
-
读读并发:多个事务同时对同一数据进行读操作。这种场景不需要使用并发控制机制,可以提高数据库系统的并发性能。
-
隔离级别并发:不同事务之间的隔离级别不同,可能会导致数据不一致的问题。这种场景需要使用事务隔离级别机制来保证数据的一致性和完整性。
-
死锁并发:多个事务之间相互依赖,可能会出现死锁的情况。这种场景需要使用死锁检测和解决机制来避免死锁的发生。
而 MVCC 主要解决数据库系统中的并发控制问题。在多用户并发访问数据库时,如果不进行并发控制,可能会导致数据不一致的问题。传统的并发控制方式是使用锁定机制,即在读取或修改数据时,需要先对数据行进行加锁,防止其他事务同时对该数据行进行修改。这种方式虽然可以保证数据的一致性,但是会降低并发性能,因为同时只有一个事务可以对数据行进行操作。
MVCC 技术通过在每个数据行上保存多个版本的数据,每个版本都有一个时间戳,来实现并发控制。当一个事务需要读取数据时,会根据该事务的时间戳选择合适的数据版本进行读取,从而避免了读取数据时的锁定操作。同时,MVCC 技术还可以通过回滚日志和垃圾回收机制来保证数据的一致性和完整性。MVCC 技术可以提高数据库系统的并发性能,同时保证数据的一致性。
MVCC + 悲观锁:MVCC 解决读写冲突,悲观锁解决写写冲突
MVCC + 乐观锁:MVCC解决读写冲突,乐观锁解决写写冲突
4、MVCC 的实现原理
MVCC 的实现原理主要是在每个数据行上保存多个版本的数据,并通过版本号和时间戳来实现并发控制。具体实现步骤如下:
-
在每个数据行上保存多个版本的数据。每个版本都有一个唯一的版本号和时间戳,用于标识该版本的数据是在何时被修改的。
-
当一个事务需要读取数据时,会根据该事务的时间戳选择合适的数据版本进行读取。如果该事务的时间戳早于某个数据版本的时间戳,则该数据版本对该事务不可见,因为该数据版本是在该事务开始之后被修改的。
-
当一个事务需要修改数据时,会先将原始数据行复制一份,并在新复制的数据行上进行修改。同时,该事务会生成一个新的版本号和时间戳,并将新版本的数据行插入到数据库中。这样,原始数据行和新数据行就形成了一个版本链。
-
当一个事务提交时,会将该事务所修改的数据行的最新版本号和时间戳更新到事务提交记录中。这样,其他事务就可以根据该事务的提交记录来选择合适的数据版本进行读取。
-
当一个事务回滚时,会将该事务所修改的数据行的最新版本号和时间戳恢复到事务开始时的状态。这样,其他事务就可以根据该事务的回滚记录来选择合适的数据版本进行读取。
-
为了避免版本链过长,数据库系统会定期进行垃圾回收,删除不再需要的版本。这样可以减少数据库系统的存储空间和提高查询性能。
为了解决读写冲突主要是依赖记录中的 4个隐式字段,undo日志 ,Read View 来实现的
4.1、4个隐式字段
MVCC(多版本并发控制)是一种用于数据库系统的并发控制技术,它通过在每个数据行上保存多个版本的数据,每个版本都有一个时间戳,来实现并发控制。在 MVCC 中,每个数据行都有四个隐式字段,分别是:
- Transaction ID:事务 ID,表示修改该数据行的事务的 ID。
- Rollback Pointer:回滚指针,指向该事务的回滚日志,用于撤销该事务对该数据行的修改。
- Row Start:行开始时间戳,表示该数据行的版本开始时间。
- Row End:行结束时间戳,表示该数据行的版本结束时间。
这四个隐式字段是 MVCC 技术实现并发控制的关键,它们记录了每个数据行的修改历史和版本信息,用于支持并发事务的读写操作。当一个事务需要读取数据时,会根据该事务的时间戳选择合适的数据版本进行读取,从而避免了读取数据时的锁定操作。同时,MVCC 技术还可以通过回滚日志和垃圾回收机制来保证数据的一致性和完整性。
4.2、undo 日志
Undo 日志是数据库系统中的一种重要的日志记录机制,用于支持事务的回滚和 MVCC(多版本并发控制)技术的实现。当一个事务执行修改操作时,数据库系统会将修改前的数据记录到 Undo 日志中,以便在事务回滚时可以恢复到修改前的状态。同时,在 MVCC 技术中,Undo 日志还用于保存每个数据行的历史版本信息,以便支持并发事务的读写操作。
下面是一个使用 Undo 日志的 SQL 示例:
假设有一个表 t,其中包含两个字段 id 和 name,现在执行以下 SQL:
UPDATE t SET name = 'new_name' WHERE id = 1;
执行该 SQL 语句时,数据库系统会将 id 为 1 的数据行修改前的值记录到 Undo 日志中。如果事务回滚,则可以使用 Undo 日志将该数据行恢复到修改前的状态。
另外,如果该表使用了 MVCC 技术,在每个数据行中也会保存 Undo 日志,用于支持并发事务的读写操作。当一个事务需要读取数据时,会根据该事务的时间戳选择合适的数据版本进行读取。如果需要回滚该事务,则可以使用 Undo 日志将该数据行恢复到指定的历史版本。
4.3、Read View
Read View 是 MySQL 中用于实现 MVCC 的一种机制,用于支持并发事务的读操作。在 MySQL 中,每个事务都有自己的 Read View,用于确定该事务可以读取到哪些数据版本。Read View 由以下两个部分组成:
- Trx ID 列表:该列表记录了当前事务启动时,已经提交的事务 ID 列表。
- 快照版本号:该版本号记录了当前事务启动时,数据库系统的快照版本号。
在执行读操作时,MySQL 会将当前事务的 Read View 与数据行的版本信息进行比较,从而确定当前事务可以读取哪些数据版本。如果数据版本早于当前事务的快照版本号或者是由未提交的事务所产生的版本,则当前事务无法读取该数据版本。
以下是一个示例 SQL 语句,说明 Read View 的工作原理:
-- 事务 A
START TRANSACTION;
SELECT * FROM t WHERE id = 1;
-- 事务 B
START TRANSACTION;
UPDATE t SET name = 'new_name' WHERE id = 1;
COMMIT;
-- 事务 A
SELECT * FROM t WHERE id = 1;
COMMIT;
在上述示例中,事务 A 在启动时会创建自己的 Read View,并记录当前数据库系统的快照版本号。在第一个 SELECT 语句中,事务 A 会根据自己的 Read View 读取 id 为 1 的数据行,此时由于事务 B 已经对该数据行进行了修改,因此事务 A 无法读取到该数据行的旧版本。
在事务 B 中,执行 UPDATE 语句时,会创建一个新的数据行版本,并将该版本的事务 ID 记录到 redo 日志中。同时,由于该版本的事务 ID 还未提交,因此该版本不会被事务 A 的 Read View 所包含。
在事务 A 中的第二个 SELECT 语句中,由于该语句在事务 A 启动之后执行,因此会使用事务 A 的当前 Read View 进行读取。由于事务 A 的 Read View 不包含事务 B 所提交的事务 ID,因此事务 A 可以读取到 id 为 1 的数据行的旧版本。
5、使用 MVCC 时,需要注意什么问题
-
事务启动时间:事务的启动时间会影响到事务能够读取到哪些数据版本。如果事务启动时间早于某个数据版本的创建时间,则该事务可以读取到该数据版本。因此,需要确保事务启动时间在需要读取的数据版本之后。
-
版本回收:MVCC 技术需要维护多个数据版本,因此需要定期清理不再需要的版本,避免占用过多的存储空间。在 MySQL 中,使用 purge 线程来定期清理不再需要的版本。
-
长事务:长时间运行的事务会占用大量的 MVCC 版本,导致存储空间不足或性能下降。因此,需要避免长时间运行的事务,或者使用合适的事务隔离级别来减少 MVCC 版本的数量。
-
并发度:MVCC 技术可以提高数据库系统的并发性能,但是也需要考虑并发度的问题。如果并发度过高,可能会导致 MVCC 版本的数量过多,从而影响性能。
-
事务隔离级别:在使用 MVCC 技术时,需要根据具体的业务需求选择合适的事务隔离级别。不同的隔离级别会影响 MVCC 版本的数量和读取的数据版本,从而影响并发性能和数据一致性。需要根据具体的业务需求进行选择。
💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊