mysql 面试知识点笔记(七)RR如何避免幻读及非阻塞读、范式

表象:快照读(非阻塞读)--伪MVCC (Multi-Version Concurrent Controll多版本并发控制)

内在:next-key锁(record锁+gap锁) rr serializabel 都支持gap锁

问:在rr下对主键索引或者唯一索引会用gap锁嘛?

如果where条件全部命中,则不会用gap锁,只会加record锁(行锁)

如果部分命中或全不命中则会加gap锁

比如查询id in(1,3,5) ,id是主键。 查到了 1、3 ,没有查到5则会加gap锁,在3~+∞的区间。

如果加锁是主键之外的索引 则会在当前索引以及主键索引上都上排他锁。如下图:

359bfe348344a93b69dbb265e052baa9cd7.jpg

-- 创建测试表
CREATE TABLE `tb`(
`name` varchar(10) NOT NULL,
`id` int(100) NOT NULL DEFAULT '0',
PRIMARY KEY(`name`),
UNIQUE KEY `unique_id` (`id`)
) ENGINE = INNODB DEFAULT CHARSET=utf8;

插入以下数据:

b640726391f5988c3c14fc58b5f695cf8c9.jpg

不加gap锁的情况

-- 第一个会话
SELECT @@tx_isolation;
set SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

start TRANSACTION;
DELETE from tb  where id = 9;
-- 执行完delete之后执行事务2的insert 是成功的 因为id=9存在 只加了行锁
ROLLBACK;

-- 第二个会话
SELECT @@tx_isolation;
set SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

start TRANSACTION;
insert into tb VALUES('i',10);
ROLLBACK;

加gap锁的情况

-- 1、全不命中
-- 第一个会话
start TRANSACTION;
DELETE from tb  where id = 7;
-- 这里在执行事务2的insert 则会block 证明加了gap锁
ROLLBACK;

-- 第二个会话
start TRANSACTION;
insert into tb VALUES('i',8);
ROLLBACK;

-- 2、部分命中情况
-- 第一个会话
start TRANSACTION;
-- 这里命中5和9 7没有命中 在6~8加了gap锁 如果是in (5,6,9)  则不会加gap锁
select * from tb where id in (5,7,9) lock in share mode;
ROLLBACK;

-- 第二个会话
start TRANSACTION;
-- 没有block
insert into tb VALUES('i1',4);
-- 有block
insert into tb VALUES('i2',7);
-- 有block
insert into tb VALUES('i3',8);
-- 没有block
insert into tb VALUES('i4',10);
ROLLBACK;
  1. Gap锁还会用在非唯一索引或者不走索引的当前读中
  • 非唯一索引cad036ae631f6db6861e1556ada26f08724.jpg都是到点的左开右闭区间

测试表 非唯一索引

CREATE TABLE `tb1`(
`name` varchar(10) NOT NULL,
`id` int(100) NOT NULL DEFAULT '0',
PRIMARY KEY(`name`),
KEY `non_unique_id` (`id`)
) ENGINE = INNODB DEFAULT CHARSET=utf8;

702ebe9b98ed58f7dbc5077f5db746d2789.jpg

-- 第一个会话
start TRANSACTION;
DELETE from tb1  where id = 9; 
-- 此时执行会话2的insert 会被block
ROLLBACK;

-- 第二个会话
start TRANSACTION;
insert into tb1 VALUES('test',9);
#6-11的左开右闭区间
-- 不会block
insert into tb1 VALUES('test1',5);
-- 会block
insert into tb1 VALUES('test2',7);
-- 不会block
insert into tb1 VALUES('test3',12);
-- bb是主键c的左边 所以不会block
insert into tb1 VALUES('bb',6);
-- dd是主键c的右边 所以会block
insert into tb1 VALUES('dd',6);
-- e是主键f的左边 所以不会block
insert into tb1 VALUES('e',11);
-- g是主键f的右边 所以会block
insert into tb1 VALUES('g',11);
ROLLBACK;
  • 不走索引(会对所有的gap加锁 就是锁表了)32b5096f0c10680e22d4a556bf72ed933f3.jpg

测试表 无索引

CREATE TABLE `tb2`(
`name` varchar(10) NOT NULL,
`id` int(100) NOT NULL DEFAULT '0',
PRIMARY KEY(`name`)
) ENGINE = INNODB DEFAULT CHARSET=utf8;

c39af51a10e73b7e2ebc0915fc7a39a44c6.jpg

-- 第一个会话
start TRANSACTION;
DELETE from tb2  where id = 9; 
ROLLBACK;

-- 第二个会话
start TRANSACTION;
-- 锁表 这里的insert是block的
insert into tb2 VALUES('test',2);
ROLLBACK;

MySQL InnoDB非阻塞式读的实现原理:MVCC

MySQL InnoDB使用MVCC来实现非阻塞式读,在这个模式下,数据库会为每个数据记录维护多个版本。在可重复读隔离级别下,事务第一次查询记录的时候,会记录下一个时间点,在该事务内如果再次(可是以不同的SELECT)查询相同的数据的话,事务只会取时间点前的记录版本,这样在不需要对数据加锁的情况下就实现了可重复读的隔离级别了,而且并发性能更好。在同一事务内多次查询同一数据,也不是就返回一个固定的记录版本,如果事务先查询了某个记录,随后自己又更新了这个数据,等再查询该数据的话,返回的就是自己更新过后的数据版本了。此处来自:https://ouyanggod.iteye.com/blog/2165668

范式参考:https://www.cnblogs.com/gdwkong/p/9012262.html

转载于:https://my.oschina.net/pentakill/blog/3045452

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值