MySQL之MVCC与幻读

 1. mysql快照读和当前读是啥含义?

在MySQL中,"快照读"(Snapshot Read)和"当前读"(Current Read)是两种不同的读取数据的方式。

1. 快照读(Snapshot Read):快照读是指在读取数据时使用数据库中某个特定时间点的快照或镜像。这意味着无论其他事务是否正在修改数据,快照读都会返回一个一致性的数据视图。快照读主要用于读取一致性要求较高的查询,以获取一个确定的数据状态。

2. 当前读(Current Read):当前读是指在读取数据时直接读取数据库中最新的数据。当前读会获取最新的数据状态,但可能受到其他并发事务的修改影响,可能读取到未提交的数据或已提交但尚未持久化到磁盘的数据。当前读主要用于读取数据后需要进行进一步的修改或处理的情况。

在MySQL中,默认情况下,使用的是当前读(Current Read),以保证读取到最新的数据。但是,可以通过设置事务隔离级别或使用特定的读取操作来实现快照读(Snapshot Read)的功能,以满足特定的读取要求。

 

如果是快照度,直接采用MVCC,如果是当前读,才会走next-key lock 

 

下面转自 https://blog.csdn.net/qq_31930499/article/details/110393988

 

2. 什么是MVCC?

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

是通过保存数据在某个时间点的快照来实现并发控制的。也就是说,不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。

可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。MVCC将数据库的行锁与行的多个版本结合起来,只需要很小的开销就可以实现读写不加锁,从而大大提高数据库系统的并发性能。

注意

写写情况还是需要加锁阻塞的。

MVCC并没有解决幻读问题。

3. MVCC解决什么问题?

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

 

4. MVCC如何工作?

MVCC保存某个时间点上的数据快照。一个事务内,看到的是同一个版本的快照,数据一致。不同事务在同一时间点看到的数据会不一致,因为他们得到的数据版本不一样。不同存储引擎的MVCC实现是不同的,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。

隐藏字段
在InnoDB中,每个数据行记录除了一般数据列还包含下面几个隐藏字段:

DB_ROW_ID :6-byte,隐藏的行 ID,用来生成默认聚簇索引。如果我们创建数据表的时候没有指定聚簇索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。

DB_TRX_ID :6-byte,操作这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。这也是下面所说的修改版本号和删除版本号。

注意有1 bit用来标志是否被删除。

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

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

undo log
undo log即为回滚日志,每次事务在执行DML语句,便会将相反的DML写入undo log,等到事务RollBack时使数据恢复到事务最开始状态。

但是undo log除了回滚,还有一个作用:当前事务读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

当隔离级别是REPEATABLE READ时,这种策略下,select、delete、 insert、 update语句如何操作:

SELECT 对于select语句,只有同时满足了下面两个条件的行,才能被返回:
行的修改版本号小于或者等于该事务号:这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
行的删除版本号要么没有被定义,要么大于事务版本号:
行的删除版本号如果没有被定义,说明该行没有被删除过;
如果删除版本号大于事务版本号,说明该行是被该事务后面启动的事务删除的,由于是repeatable read隔离等级,后开始的事务对数据的影响不应该被先开始的事务看见,所以该行应该被返回。
INSERT 对新插入的行,行的更新版本号被修改为该事务的事务号。
DELETE 对于删除,直接把该行的删除版本号设置为当前的事务号,相当于标记为删除,而不是实际删除。
UPDATE 在更新行的时候,InnoDB会把原来的行复制一份到回滚段中,并把当前的事务号作为该行的更新版本号,同时保存当前事务版本号到原来的行作为行删除标识:保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

posted @ 2021-08-31 08:11  daniel456  阅读(242)  评论(0编辑  收藏  举报