InnoDB下的MVCC

MVCC,是采用乐观锁思想的一种方式;

在 MySQL 中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题,但不能解决幻读问题。

如果想要解决幻读问题,就需要采用串行化的方式,也就是将隔离级别提升到最高,但这样一来就会大幅降低数据库的事务并发能力。

我们也可以采用MVCC+Next-Key Lock的方式来解决幻读问题。

MVCC 是什么

MVCC 的英文全称是 Multiversion Concurrency Control,即多版本并发控制技术。

MVCC 是通过数据行的多个版本管理来实现数据库的并发控制,简单来说它的思想就是保存数据的历史版本。

这样就可以通过比较版本号决定数据是否显示出来,读取数据的时候不需要加锁也可以保证事务的隔离效果。

MVCC可以解决什么

  1. 读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。
  2. 降低了死锁的概率。这是因为 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。
  3. 解决一致性读的问题。一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

快照读:快照读读取的是快照数据,不加锁的简单的 SELECT 都属于快照读。

当前读:当前读读取最新数据,而不是历史版本的数据。加锁的 SELECT,或者对数据进行增删改都会进行当前读。

快照读就是普通的读操作,而当前读包括了加锁的读取和 DML 操作。

InnoDB 中实现MVCC

 MVCC 没有正式的标准,所以在不同的 DBMS 中,MVCC 的实现方式可能是不同的。

InnoDB 是如何存储记录的多个版本

事务版本号:每开启一个事务,我们都会从数据库中获得一个事务 ID(也就是事务版本号),这个事务 ID 是自增长的,通过 ID 大小,我们就可以判断事务的时间顺序。

行记录的隐藏列:InnoDB 的叶子段存储了数据页,数据页中保存了行记录,而在行记录中有一些重要的隐藏字段。

db_row_id:隐藏的行 ID,用来生成默认聚集索引。如果我们创建数据表的时候没有指定聚集索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚集索引的方式可以提升数据的查找效率。

db_trx_id:操作这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。

db_roll_ptr:回滚指针,也就是指向这个记录的 Undo Log 信息。

Undo Log:InnoDB 将行记录快照保存在了 Undo Log 里,我们可以在回滚段中找到它们。

 从图中能看到回滚指针将数据行的所有快照记录都通过链表的结构串联了起来,每个快照的记录都保存了当时的 db_trx_id,也是那个时间点操作这个数据的事务 ID。

这样如果我们想要找历史快照,就可以通过遍历回滚指针的方式进行查找。

Read View 的工作机制

在 MVCC 机制中,多个事务对同一个行记录进行更新会产生多个历史快照,这些历史快照保存在 Undo Log 里。

如果一个事务想要查询这个行记录,需要读取哪个版本的行记录呢?这时就需要用到 Read View 了,它帮我们解决了行的可见性问题。

Read View 保存了当前事务开启时所有活跃(还没有提交)的事务列表,可以理解为 Read View 保存了不应该让这个事务看到的其他的事务 ID 列表。

在 Read VIew 中有几个重要的属性:

  • trx_ids,系统当前正在活跃的事务 ID 集合。
  • low_limit_id,活跃的事务中最大的事务 ID。
  • up_limit_id,活跃的事务中最小的事务 ID。
  • creator_trx_id,创建这个 Read View 的事务 ID。

假设当前有事务 creator_trx_id 想要读取某个行记录,这个行记录的事务 ID 为 trx_id,那么会出现以下几种情况。

如果 trx_id < 活跃的最小事务 ID(up_limit_id),也就是说这个行记录在这些活跃的事务创建之前就已经提交了,那么这个行记录对该事务是可见的。

如果 trx_id > 活跃的最大事务 ID(low_limit_id),这说明该行记录在这些活跃的事务创建之后才创建,那么这个行记录对当前事务不可见。

如果 up_limit_id < trx_id < low_limit_id,说明该行记录所在的事务 trx_id 在目前 creator_trx_id 这个事务创建的时候,可能还处于活跃的状态,因此我们需要在 trx_ids 集合中进行遍历,如果 trx_id 存在于 trx_ids 集合中,证明这个事务 trx_id 还处于活跃状态,不可见。否则,如果 trx_id 不存在于 trx_ids 集合中,证明事务 trx_id 已经提交了,该行记录可见。

系统如何通过多版本并发控制技术查询记录:

  1. 首先获取事务自己的版本号,也就是事务 ID;
  2. 再获取 Read View;
  3. 查询得到的数据,然后与 Read View 中的事务版本号进行比较;
  4. 如果不符合 ReadView 规则,就需要从 Undo Log 中再获取历史快照;
  5. 直到返回符合规则的数据。

在InnoDB 中,MVCC 是通过 Undo Log + Read View 进行数据读取,Undo Log 保存了历史快照,而 Read View 规则帮我们判断当前版本的数据是否可见。

在隔离级别为读已提交(Read Commit)时,一个事务中的每一次 SELECT 查询都会获取一次 Read View。

当隔离级别为可重复读的时候,就避免了不可重复读,这是因为一个事务只在第一次 SELECT 的时候会获取一次 Read View,而后面所有的 SELECT 都会复用这个 Read View。

 InnoDB如何用解决幻读 

在可重复读的情况下,InnoDB 可以通过 Next-Key 锁(解决悲观锁方式)或 MVCC(解决乐观锁方式) 来解决幻读问题。

在读已提交的情况下,即使采用了 MVCC 方式也会出现幻读(读已提交的情况下,readview会更新,读取到新的记录)。

如果我们同时开启事务 A 和事务 B,先在事务 A 中进行某个条件范围的查询,读取的时候采用排它锁,在事务 B 中增加一条符合该条件范围的数据,并进行提交,然后我们在事务 A 中再次查询该条件范围的数据,就会发现结果集中多出一个符合条件的数据,这样就出现了幻读。

InnoDB 三种行锁的方式:

  • 记录锁:针对单个行记录添加锁。
  • 间隙锁:可以帮我们锁住一个范围(索引之间的空隙),但不包括记录本身。采用间隙锁的方式可以防止幻读情况的产生。
  • Next-Key 锁:帮我们锁住一个范围,同时锁定记录本身,相当于间隙锁 + 记录锁,可以解决幻读的问题。

在隔离级别为可重复读时(要给记录加锁),InnoDB 会采用 Next-Key 锁(间隙锁 + 记录锁)的机制,帮我们解决幻读问题。

当事务B要增加一条范围内的数据时,B 会超时,无法插入该数据。因为采用了 Next-Key 锁,会将范围内索引之间的空隙都进行锁定,就无法插入符合这个范围的数据了。然后事务 A 重新进行条件范围的查询,就不会出现幻读的情况。


综上:想要解决乐观锁的幻读问题,使用可重复读级别的mvcc。

想要解决悲观锁幻读问题,使用 Next-Key 锁。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
InnoDBMVCC(多版本并发控制)是一种用于实现并发访问的机制,它可以提供一定级别的一致性读取。在 InnoDB 中,每个新插入的行都会保存一个当前系统版本号作为行版本号,每个删除的行也会保存一个当前系统版本号作为行删除标识。对于更新操作,InnoDB 会插入一行新记录,并保存当前系统版本号作为行版本号,同时在原来的行上保存当前系统版本号作为删除标识(实际上是通过undo log来备份旧记录)。这样,对于读取操作,可以通过生成一个数据请求时间点的一致性数据快照(Snapshot)来提供一定级别的一致性读取,并且数据库可以提供同一数据的多个版本。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [InnoDB下的MVCC](https://blog.csdn.net/shade7/article/details/119939547)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [InnoDBMVCC的实现](https://blog.csdn.net/fuzhongmin05/article/details/91351933)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值