JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

可重复读是如何规避幻读的&又是为什么不能完全解决幻读

wys521 2024-12-05 15:43:47 精选教程 16 ℃ 0 评论

相关的基础知识点就不再详述了,网上资料很多。也可看

MVCC知识点总结

可重复读(下文简称RR)理论上是无法解决幻读的,但通过多版本并发控制(下文简称MVCC)、排它锁(下文简称X锁)、临键锁(Next-Key锁,下文简称NK锁)等去尽量地规避幻读。


一、如何规避幻读。

MVCC有两种读取方式:快照读、当前读。

1、如果只是查询,例如

SELECT * FROM table;

这就是快照读,因为MVCC暗含创建版本号(下文简称CV),删除版本号(下文简称DV),SQL语句暗含了

WHERE cv<=当前版本号(即当前事务ID) AND (dv is null OR dv>当前版本号)

后面事务插入的数据读不到,这样就不会产生幻读。

2、如果涉及增删改,就使用当前读。读取最新的数据进行处理,即便是后面事务已提交的数据。例如事务1要更新记录A,但是事务2已经删除了记录A,这就会出问题。

由上可见,当前读是避免不了幻读的。因此加入了NK锁来规避。

假设有字段a=10、20、40、60、70几条记录。

事务1:

SELECT * FROM table WHERE a=40 FOR UPDATE;

这时候InnoDB锁的不仅仅是40这条记录,还会锁(20,40]、(40,60]这两个间隙(GAP)。

事务2,如果INSERT 11或者61,都能成功,但是如果INSERT 21、41就会失败。

所以通过NK锁的排他性,既保证了相关可能产生幻读的记录进不来,也保证了与之无关的数据可以INSERT,提高并发。


二、无法完全避免幻读。

设有如下记录,

id

name

age

cv

dv

1

A

10

10

-

2

B

10

10

-

事务100:

SELECT * FROM table WHERE age=10;

查询结果为2条记录。

事务200:

INSERT INTO table VALUES (3,C,10);

此时表数据变为:

id

name

age

cv

dv

1

A

10

10

-

2

B

10

10

-

3

C

10

200

-

事务100:

SELECT * FROM table WHERE age=10;

查询结果仍为2条记录。因为这两次查询都是快照读。

事务100:不加任何条件,将表所有name改为D,

UPDATE table SET name='D';

因为是当前读,所以修改会作用在所有事务提交的数据上,包括事务200新增的数据3,此时表数据变成了:

id

name

age

cv

dv

1

A

10

10

100

2

B

10

10

100

3

C

10

200

100

1

D

10

100

-

2

D

10

100

-

3

D

10

100

-

事务100:

SELECT * FROM table WHERE age=10;

查询结果为3条记录,出现了幻读。

同理,事务150本来读的是2条,事务100更新之后,也会读出来3条。

事务100前两个SELECT都是快照读,无锁,事务200可以插入,但UPDATE之后,或者假如两个SELECT加了FOR UPDATE,有了NK锁,事务200就无法插入了。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表