你好,是我琉忆。我们上一节聊了锁,这次我们聊聊脏读和不可重复读。这个都是面试中爱问的知识点。所以本次的知识点记得捡起来哦!

一般是通过锁机制,解决掉不可重复读和幻读的问题。是不是可以通过乐观锁的问题去解决不可重复读和幻读的问题,MySQL 采用的是 MVCC 机制来解决脏读、不可重复读的问题。

MVCC 英文全称是 Muitiversion Concurrency Control,多版本并发控制技术,原理是通过数据行的多个版本管理实现数据库的并发控制,通过保存数据的历史版本,可以通过比较版本号决定数据是否显示,读取数据的时候不需要加锁保证事务的隔离效果。

MVCC 是如何解决脏读的?

MVCC 解决了一致性读问题,当我们读取某个数据库在时间点的快照时,只能看到时间点之前提交更新的结果,不能看到时间点之后事务提交的更新结果。这样就避免了脏读问题。

MVCC 是如何解决不可重复读的?

在说如何解决不可重复读之前,先谈谈 MVCC 的实现原理。

快照读

快照读,读取的是快照数据,一般不加锁的 select 查询都是快照读。

select * from  player where ...

当前读

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

SELECT * FROM player LOCK IN SHARE MODE;
SELECT FROM player FOR UPDATE;
INSERT INTO player values ...
DELETE FROM player WHERE ...
UPDATE player SET ...

MVCC 是基于 Read View + 活跃事务表实现的。

行记录快照模型

数据表中的一行记录实际上有多个版本,每个版本有自己的事务 ID(row_trx_id)每次需要读取那个版本数据的时候,是通过当前版本,加 Undo Log 中存储的快照链表得到那个版本真实数据。

mysql 不可重复读幻读问题 mysql不可重复读危害_不可重复读有什么影响

行记录快照是保存在 Undo Log 中,并且行记录快照是通过链表串联起来,每个快照都保存了 trx_id (事务 ID),如果要找到历史快照,只需要遍历回滚指针进行查找。

Read View

会有这么一个问题:一个事务开启,这个事务要查询数据,需要读取哪个版本的行记录呢?Read View 就是来解决这个问题的,简单的说 Read View 是可以帮助我们解决可见性问题的。

ReadView 中保存了当前活跃的事务列表。通过比较事务版本,可以判断当前行数据版本是不是对当前事务可见。

Read View 模型

mysql 不可重复读幻读问题 mysql不可重复读危害_幻读和不可重复读区别_02

如果当前行记录版本是 trx_id < 活跃的最小事务 ID (up_limit_id),说明行记录在这些活跃的事务创建前就已经提交,这个行记录对当前事务是可见的。

如果当前行记录事务版本 trx_id > 活跃的最大事务 ID (low_limit_id) 说明,这个行记录是在事务后创建的,这个行记录对当前事务不可见。

如果 up_limit_id < trx_id < low_limit_id,则有如下情况:

  1. 若 row trx_id 在数组 trx_ids 中,表示这个版本是由还没提交的事务生成的,不可见。
  2. 若 row trx_id 不在数组 trx_ids 中,表示这个版本是已经提交了的事务生成的,可见。

mysql 不可重复读幻读问题 mysql不可重复读危害_幻读和不可重复读区别_03

后话

    在使用数据库的时候事务是被用得很多的,那么就避免不了遇到脏读、不可重复读的问题。自然我们需要对这个问题进行解决。所以这个也是不可或缺的一个技能!