思考:为什么要用MVCC?我直接使用读写锁来实现数据的一致性不行吗?
很明显是不行的,在高并发的数据库应用中,同时有大量的读写操作。传统的锁机制(如读写锁)在高并发场景下可能会导致大量的锁争用,降低系统性能。
MVCC通过维护数据的多个版本,允许读操作和写操作并发进行,减少了锁争用,从而提高系统的并发性能。
文章目录
1.当前读和快照读
1.1当前读
当前读是指读取数据时,读取的是最新的数据。当前读会读取最新的数据版本,并且可能需要等待其他事务释放锁。
当前读的场景包括:
SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE
INSERT
UPDATE
DELETE
这些操作会获取行级锁,以确保读取的是最新的数据,并防止其他事务修改读取的数据行。
实现方式:当前读依赖于行级锁和事务机制,以确保读取的是最新的数据版本,并防止其他事务在读取过程中修改数据。
1.2快照读
快照读是指读取数据时,读取的是数据在某个时间点的快照版本。InnoDB存储引擎通过多版本并发控制(MVCC)来实现快照读,读取的数据可能不是最新的数据,而是事务开始时的快照数据。
快照读是默认的SELECT读操作,在事务隔离级别为可重复读(REPEATABLE READ)**或更低(例如**读已提交(READ COMMITTED))时,执行普通的SELECT语句都是快照读。
快照读的优点:
- 不需要等待锁,避免了读写操作的冲突,提高了并发性能。
- 可以实现一致性读取,事务在整个过程中看到的数据都是一致的,不受其他事务的影响。
实现方式:快照读是通过多版本并发控制(MVCC)实现的。在MVCC机制下,每行数据都有多个版本,存储在数据页中。每个版本的数据都有两个隐藏的时间戳字段:trx_id
(事务ID)和roll_pointer
(回滚指针)。
2.MVCC是什么?
3.MVCC实现原理
3.1隐藏字段
当使用InnoDB存储引擎创建一张表时,系统会自动为每一行数据添加一些隐藏字段,以支持MVCC和事务管理。
- trx_id,当一个事务对某条聚簇索引记录进行改动时,就会把该事务的事务 id 记录在 trx_id 隐藏列里;
- roll_pointer,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,然后这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录。
3.2undo版本链
在MySQL的InnoDB存储引擎中,Undo版本链(Undo Version Chain)是多版本并发控制(MVCC)机制的重要组成部分。Undo版本链通过维护数据的历史版本,支持事务的一致性读取和回滚操作。每当数据被修改时,InnoDB会生成一条回滚日志记录,并将其加入到版本链中,从而形成一个历史版本链条。
3.3readview
Read View 到底是个什么东西?
Read View 有四个重要的字段:
- m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务。
- min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值。
- max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;
- creator_trx_id :指的是创建该 Read View 的事务的事务 id。
3.4隐藏字段+undo版本链+readview 如何实现MVCC(如何决定返回什么数据)
隐藏字段 + undo版本链 ----------------------------------------->返回的数据
readview + 版本链访问规则/可见性算法 ------------------->返回数据的版本
4.MVCC概述
宏观:MySql的InnoDB存储引擎下RC、RR且快照读下基于MVCC做数据的多版本并发控制。
5.MVCC在RC和RR级别下不同表现
6.MVCC解决了什么问题
6.1解决了读写阻塞
- 在传统的数据库系统中,当一个事务正在读取数据时,如果其他事务同时对同一数据进行写操作,就会导致读-写冲突。
- MVCC通过为每个事务提供一致性的数据视图,允许事务在读取数据时不被写操作所阻塞,从而减少了读-写冲突,提高了并发性能。
6.2在RR快照读下解决了幻读问题(大部分)
可重复读隔离级是由 MVCC(多版本并发控制)实现的,实现的方式是开始事务后(执行 begin 语句后),在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好了避免幻读问题。
思考:那在RR级别下,当前读没有采用MVCC,那他是如何解决幻读问题的?(MySql默认的隔离级别是RR)
Innodb 引擎为了解决「可重复读」隔离级别使用「当前读」而造成的幻读问题,就引出了间隙锁。
假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。