mysql是怎么解决幻读的(mvcc,间隙锁)

mysql 专栏收录该内容
5 篇文章 0 订阅

mysql的默认隔离级别是RR(可重复读),网上随便一查都知道RR会导致幻读(同一个事务里面多次查询的结果不一致),可是我自己测试过后发现在RR下并不存在幻读的问题,哪mysql是怎么解决幻读的呢?有两种手段。1,mvcc(多版本控制),2,范围锁

1. mvcc

每次开启事务后都会递增创建一个版本号(version),之后的增删查改都是基于这个版本号进行操作的。
SELECT (version)

  • 读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的。version >= createVersion and version < deleteVersion

INSERT (createVersion)

  • 将当前事务的版本号保存至行的创建版本号。 createVersion = version

UPDATE (createVersion)

  • 新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号。 新行createVersion = version,旧行deleteVersion = version

DELETE (deleteVersion)

  • 将当前事务的版本号保存至行的删除版本号。 deleteVersion = version

例子:

初始数据
表名:test

idnumbercreateVersiondeleteVersion
111

select * from test; //当前version = 2,由于id=1的createVersion=1小于当前version所以可以查出来。

插入一条数据

idnumbercreateVersiondeleteVersion
111
233

这时候如果上面的selct事务没结束继续查询的话并不会查询到id=2的数据。由于新插入的数据createVersion = 3 大于查询的version,这样就避免了幻读的情况。

更新一条数据,update test set number = 10 where id = 2

idnumbercreateVersiondeleteVersion
111
2336
2106

如果事务在update之前开启(如version=5)那么只能看到createVersion<=5 and deleteVersion > 5 的数据,那么看到的数据就是update前的

2. 间隙锁

mysql的间隙所是基于索引的,对于唯一索引innode会把间隙所降级为行锁,非唯一索引的话就需要用到间隙锁(也叫范围锁)

idnumber
11
23
133
233
3111
4040

事务一:select * from test where number = 3 for update
对于number索引可以分为多个范围
(无穷小,1)(1,3)(3,3)(3,11)(11,无穷大)
这时候锁住的是(3,3)区间,对应的临界记录是(id=1,number=1)(id=31,number=11),对于这范围内的数据都是被锁住的。

事务二:insert into test(id, number) value(5, 3) //是会被阻塞

事务三:insert into test(id, number) value(25, 4) //也是会被阻塞

事务四:insert into test(id, number) value(35, 4) //也是会被阻塞

事务五:insert into test(id, number) value(22, 12) //插入成功 (因为12>11所以在锁区间外)

事务六:insert into test(id, number) value(71, 11) //插入成功 (number值一样,但是id71>31所以在锁区间外)

  • 3
    点赞
  • 1
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值