数据库(四):多版本并发控制MVCC,行锁的衍生版本,记录锁,间隙锁, Next-Key锁(邻键锁)

文章目录

  • 前言
  • 一、MVCC以及MVCC的缺点
    • 1.1 MVCC可以为数据库解决什么问题
    • 1.2 MVCC的基本思想
    • 1.3 版本号
    • 1.4 Undo日志
    • 1.5 ReadView
    • 1.6 快照读和当前读
      • 1.6.1 快照读
      • 1.6.2 当前读
  • 二、记录锁
  • 三、间隙锁
  • 四、邻键锁
  • 总结


前言

一、MVCC以及MVCC的缺点

MVCC,即多版本并发控制,是InnoDB机制中的一种用来解决读写冲突的无锁并发机制。

我们可以简单认为MVCC式行锁的变种。在表锁中我们的读写是阻塞的,基于提升并发性能的考虑,MVCC一般读写是不阻塞的(很多情况避免了加锁的操作)。

MVCC解决了脏读和不可重复读的问题,但是在某些情况仍然没有解决幻影读的问题。这就是为什么我们要引入接下来的邻键锁Next-Key Locks。

1.1 MVCC可以为数据库解决什么问题

在并发读写数据库的时候,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。

同时可以解决脏读和不可重复读的问题。

1.2 MVCC的基本思想

之前我们介绍了行锁和表锁,我们知道在操作数据库中的索引的时候会加上行锁,在操作数据库中的非索引字段的时候会加上表锁。然而在实际场景中读操作往往多于写操作,因此我们又引入了读写锁来避免不必要的加锁操作,例如读和读之间没有互斥关系。 读写锁中读和写操作仍然是互斥的。

MVCC中的基本思想就是写操作更新最新的版本快照,而读操作去读旧版本的快照,两者没有互斥关系。

MVCC的核心组成部分可以分为版本号,undo日志以及readview。

1.3 版本号

  1. 系统版本号 SYS_ID:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。
  2. 事务版本号 TRX_ID :事务开始时的系统版本号。

1.4 Undo日志

MVCC 的多版本指的是多个版本的快照,快照存储在 Undo 日志中,该日志通过回滚指针 ROLL_PTR 把一个数据行的所有快照连接起来。

例如在 MySQL 创建一个表 t,包含主键 id 和一个字段 x。我们先插入一个数据行,然后对该数据行执行两次更新操作。

INSERT INTO t(id, x) VALUES(1, "a");
UPDATE t SET x="b" WHERE id=1;
UPDATE t SET x="c" WHERE id=1;

因为没有使用 START TRANSACTION 将上面的操作当成一个事务来执行,根据 MySQL 的 AUTOCOMMIT 机制,每个操作都会被当成一个事务来执行,所以上面的操作总共涉及到三个事务。快照中除了记录事务版本号 TRX_ID 和操作之外,还记录了一个 bit 的 DEL 字段,用于标记是否被删除。
数据库(四):多版本并发控制MVCC,行锁的衍生版本,记录锁,间隙锁, Next-Key锁(邻键锁)_第1张图片
INSERT、UPDATE、DELETE 操作会创建一个日志,并将事务版本号 TRX_ID 写入。DELETE 可以看成是一个特殊的 UPDATE,还会额外将 DEL 字段设置为 1。

1.5 ReadView

MVCC 维护了一个 ReadView 结构,主要包含了当前系统未提交的事务列表 TRX_IDs {TRX_ID_1, TRX_ID_2, …},还有该列表的最小值 TRX_ID_MIN 和 TRX_ID_MAX。

数据库(四):多版本并发控制MVCC,行锁的衍生版本,记录锁,间隙锁, Next-Key锁(邻键锁)_第2张图片
在进行 SELECT 操作时,根据数据行快照的 TRX_ID 与 TRX_ID_MIN 和 TRX_ID_MAX 之间的关系,从而判断数据行快照是否可以使用:

  1. TRX_ID < TRX_ID_MIN,表示该数据行快照时在当前所有未提交事务之前进行更改的,因此可以使用。

  2. TRX_ID > TRX_ID_MAX,表示该数据行快照是在事务启动之后被更改的,因此不可使用。

  3. TRX_ID_MIN <= TRX_ID <= TRX_ID_MAX,需要根据数据库隔离级别再进行判断:
    3.1. 提交读(Read Commited):如果 TRX_ID 在 TRX_IDs 列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。
    3.2. 可重复读(Read Repeatable):无论TRX_ID 在不在 TRX_IDs 列表中都不可以使用。因为如果可以使用的话,那么其它事务也可以读到这个数据行快照并进行修改,那么当前事务再去读这个数据行得到的值就会发生改变,也就是出现了不可重复读问题。

在数据行快照不可使用的情况下,需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照,再进行上面的判断。

1.6 快照读和当前读

1.6.1 快照读

MVCC中的select操作是快照中的数据,不需要进行加锁操作。

1.6.2 当前读

MVCC 其它会对数据库进行修改的操作(INSERT、UPDATE、DELETE)需要进行加锁操作,从而读取最新的数据。可以看到 MVCC 并不是完全不用加锁,而只是避免了 SELECT 的加锁操作。

在进行 SELECT 操作时,可以强制指定进行加锁操作。以下第一个语句需要加 S 锁,第二个需要加 X 锁。

SELECT * FROM table WHERE ? lock in share mode;
SELECT * FROM table WHERE ? for update;

二、记录锁

三、间隙锁

四、邻键锁


总结

你可能感兴趣的:(数据库,数据库,mysql,java)