腾讯面试:MySQL 事务与 MVCC 如何实现的隔离级别?_csdn 三太子敖丙-CSDN 博客
多版本并发控制 Multi-Version ConcurrencyControl,用于在多事务并发读写数据库时保持数据的一致性和隔离性。它是通过在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。
MVCC 不对数据加锁,因此如果是非锁定读(普通的 select 读)就读取快照数据,解决部分幻读,而对于锁定读,则读取的是当前数据,就需要使用临键锁锁住区间防止数据插入。
通常对数据库的操作无非是增删改查、事务提交、回滚
读操作(SELECT)
当一个事务执行读操作时,它会使用快照读取。快照读取是基于事务开始时数据库中的状态创建的,因此事务不会读取其他事务尚未提交的修改。具体工作情况如下:
- 对于读取操作,事务会查找符合条件的数据行,并选择符合其事务开始时间的数据版本进行读取。
- 如果某个数据行有多个版本,事务会选择不晚于其开始时间的最新版本,确保事务只读取在它开始之前已经存在的数据。
- 事务读取的是快照数据,因此其他并发事务对数据行的修改不会影响当前事务的读取操作。
写操作(INSERT、UPDATE、DELETE)
当一个事务执行写操作时,它会生成一个新的数据版本,并将修改后的数据写入数据库。具体工作情况如下:
- 对于写操作,事务会为要修改的数据行创建一个新的版本,并将修改后的数据写入新版本。
- 新版本的数据带有当前事务的版本号,以便其他事务能够正确读取相应版本的数据。
- 原始版本的数据仍然存在,供其他事务使用快照读取,这保证了其他事务不受当前事务的写操作影响。
事务提交和回滚
- 当一个事务提交时,它所做的修改将成为数据库的最新版本,并且对其他事务可见。
- 当一个事务回滚时,它所做的修改将被撤销,对其他事务不可见。
版本的回收
为了防止数据库中的版本无限增长,MVCC 会定期进行版本的回收。回收机制会删除已经不再需要的旧版本数据,从而释放空间。
MVCC 通过创建数据的多个版本和使用快照读取来实现并发控制。读操作使用旧版本数据的快照,写操作创建新版本,并确保原始版本仍然可用。这样,不同的事务可以在一定程度上并发执行,而不会相互干扰,从而提高了数据库的并发性能和数据一致性。
非锁定读和锁定读
非锁定读
在
InnoDB存储引擎中,MVCC 就是对非锁定读的实现。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会等行上锁的释放,而是读取行的快照数据,对于这种读取历史数据的方式,我们叫它快照读 (snapshot read)在
Repeatable Read和Read Committed两个隔离级别下,如果是执行普通的select语句(不包括select ... lock in share mode,select ... for update)则会使用一致性非锁定读(MVCC)。并且在Repeatable Read下MVCC实现了可重复读和防止部分幻读
锁定读
如果执行的是下列语句,就是 锁定读
select ... lock in share modeselect ... for updateinsert、update、delete操作在锁定读下,读取的是数据的最新版本,这种读也被称为当前读。锁定读会对读取到的记录加锁。
在非锁定读下,即使读取的记录已被其它事务加上
X锁,这时记录也是可以被读取的,因为此时读取的是快照数据。在Repeatable Read下MVCC防止了部分幻读,这边的 “部分” 是指在 非锁定读 情况下,只能读取到第一次查询之前所插入的数据(根据 Read View 判断数据可见性,Read View 在第一次查询时生成)。但是如果是当前读,每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。所以,InnoDB在实现Repeatable Read时,如果执行的是当前读,则会对读取的记录使用 临键锁 ,来防止其它事务在间隙间插入数据。
Read View
ReadView 主要用来做可见性判断,里面保存了一些字段
读已提交和可重复读的隔离级别下 MVCC 差异
在事务隔离级别
RC和RR下, 通过MVCC解决执行普通的 SEELCT 操作,但它们生成Read View的时机却不同
- 在 RC 隔离级别下的每次
select查询前都生成一个Read View(m_ids 列表)- 在 RR 隔离级别下只在事务开始后第一次
select数据前生成一个Read View(m_ids 列表)
MVCC+临键锁防止幻读
1、执行普通
select,此时会以MVCC快照读的方式读取数据在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成
Read View,并使用至事务提交。所以在生成Read View之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”2、执行 select…for update/lock in share mode、insert、update、delete 等当前读
在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!
InnoDB使用临键锁来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读。
