mysqlmvcc解决幻读

发布时间: 2023-11-21 12:34 阅读: 文章来源:1MUMB3910PS
快照读与当前读

在RR级别下,通过MVCC机制,可以实现可重复读,但是读到的数据是历史数据,不是数据库最新数据。这种读取历史数据的方式叫快照读,而读取数据库最新版本数据的方式叫当前读。

快照读

当执行select操作时Innodb默认会执行快照读,会记录下这次select后的结果,之后select的时候就会返回这次快照的数据,即使其他事务提交也不会影响当前select的数据,从而实现可重复读。

简单的select语句就是读取快照

当前读

读取最新版本的记录,需要加锁,没有快照。

S锁

select ... lock in share mode

X锁

select ... for update

insert

update

delete

GAP锁和幻读

MVCC机制通过快照读可以解决可重复读,但是解决不了幻读,幻读需要Next-Key Lock来解决

MySQL InnoDB支持三种行锁定方式:

Record Lock:行锁,锁定一个记录上的索引,非记录本身。

Gap Lock:间隙锁,锁住索引之间范围,但不包括索引本身,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的范围。

Next-Key Lock:行锁与间隙锁的组合,

下面对RR级别下非唯一索引测试Gap锁:

表定义如下:

CREATE TABLE `test` (

`id` int(11) primary key auto_increment,

`c1` int(11) DEFAULT NULL,

`c2` int(11) DEFAULT NULL,

KEY `c1` (`c1`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into test(c1) values (10), (20), (30);

表中有3条记录:

mysql> select * from test;

+----+------+------+

| id | c1 | c2 |

+----+------+------+

| 1 | 10 | NULL |

| 2 | 20 | NULL |

| 3 | 30 | NULL |

+----+------+------+

3 rows in set (0.00 sec)

现在,该索引可能被锁住的范围如下:

(-, 10), (10, 20), (20, 30), (30, +)

Session 1:

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update test set c2=2 where c1=20;

Query OK, 1 row affected (0.02 sec)

Rows matched: 1 Changed: 1 Warnings: 0

Session 2:

mysql> begin;

mysql> insert into test (c1, c2) values(9,4);

mysql> insert into test (c1, c2) values(10,4);

mysql> insert into test (c1, c2) values(19,4);

mysql> insert into test (c1, c2) values(20,4);

mysql> insert into test (c1, c2) values(21,4);

mysql> insert into test (c1, c2) values(29,4);

mysql> insert into test (c1, c2) values(30,4);

SESSION 2中,执行插入操作,发现[10,30)范围不能插入数据。

mysql> begin;

mysql> update test set c2=4 where c1=10;

mysql> update test set c2=4 where c1=20;

mysql> update test set c2=4 where c1=30;

update除了id=20的记录以外,其他的update操作都成功了。

结论:

如果Session 1的表扫描没有用到索引,那么gap或next-key锁住的范围是整个表,即任何值都不能插入。如果表扫描用到索引,那么next-key锁住的范围是该索引以及该索引前后的gap,这个范围内不可以插入值,但可以更新值(除了被锁住的那条记录)。

但是为什么数据10被锁住了,而数据30却没有呢?InnoDB只锁定必要的范围,这个必要范围与索引排序规则有关系,InnoDB默认的索引排序规则是asc,也就是说,相同的索引数据是往后排的。

一开始索引排序后是这么排列的:10 20 30,按照next-key锁的原则,(10,30)这个范围会被锁定,当后续再插入10和30这2条数据时,按照相同索引数据往后排的原则,此时的索性排序: 10 10 20 30 30,后插入的10和30都会在之前10和30的后面,所以10可以插入,30却不能,最后锁定的范围就是[10, 30)。

•••展开全文
相关文章