庶民村村长 发表于 2023-6-25 18:49:52

锁(case篇)

case1(表锁的读-写-读阻塞)

上篇文档中提到过
WRITE locks normally have higher priority than READ locks to ensure that updates are processed as soon as possible. This means that if one session obtains a READ lock and then another session requests a WRITE lock, subsequent READ lock requests wait until the session that requested the WRITE lock has obtained the lock and released it.
对于读-写-读的情况,由于锁的优先级较高,如果申请写的session迟迟获取不到锁,会阻塞后续其他session申请读锁;
先看正常情况,表锁的读锁是可以加多个的,如下,通过两个查询命令也可以看到确实同时加上了,没有阻塞;
//console1
lock tables simple read;
//console2
lock tables simple read;select * from performance_schema.metadata_locks;

show OPEN TABLES where In_use > 0;

但是在两次读中间插入一次写锁的获取,后面的读锁也会同时被阻塞
//console1
lock tables simple read;
//console2
lock tables simple write;//被console1阻塞
//console3
lock tables simple read;//被console2阻塞实验证明确实如文档所说,原理还在研究中...
case2(元数据锁读-写-读)

mysql45讲中提到的一个问题,具体分析见mysql MDL读写锁阻塞,以及online ddl造成的“插队”现象_花落的速度的博客-CSDN博客

case3(next-key lock 和 primary key)

在分析之前,先贴一下45讲的总结,该总结版本是 5.x 系列 5 lock in share mode ;select * from performance_schema.data_locks;//console2都会被阻塞insert into simple (id,name,type,seq) value (6,5,5,5);insert into simple (id,name,type,seq) value (36,5,5,5);
id>=5

和上面的唯一区别就是多了个等于5,那么5上是临键锁还是行锁呢?我觉得是行锁,因为优化1,而且这样和我们的认知也是比较符合的;
实际看到确实是这样;
CREATE TABLE `simple` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '字符',
`seq` bigint NOT NULL COMMENT '消息序号',
`type` tinyint NOT NULL COMMENT '类型,tinyint值',
`version` int NOT NULL DEFAULT '1' COMMENT '版本值',
`msg` text COLLATE utf8mb4_bin COMMENT '消息',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`yn` tinyint NOT NULL DEFAULT '1' COMMENT '是否有效',
`uni` int NOT NULL COMMENT '唯一索引',
PRIMARY KEY (`id`),
UNIQUE KEY `unidx` (`uni`),
KEY `seqidx` (`seq`)
) ENGINE=InnoDB AUTO_INCREMENT=301 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='简单测试表'
id>5 and id105 and uni105 and uni105 and uni105 and uni130和上面的id>30结果一样,就不贴了

总结:对于唯一索引来说,因为存在主键,那么会产生回表操作,回表操作会给主键再加一把锁;而那个bug依旧存在,只有主键索引的修复了,非主键唯一索引依然存在这个bug;
case5(索引加在哪)


start transaction ;
select * from simple where id = 15 lock in share mode ;
select * from performance_schema.data_locks;现在我们已经清楚,执行完console1之后,会给unidx加一个行锁,因为没有回表,所以主键上没有锁;那么console2能否成功执行呢?
答案是start transaction ;
select * from simple where id = 16 lock in share mode ;
select * from performance_schema.data_locks;
//console2
start transaction;
insert into simple (id,name,type,seq) value (16,5,5,5);//会被阻塞
select * from simple where id=20 for update ;//发现这行可以执行成功我个人理解,是因为锁是加在索引上的,而索引是列维度的,不是行维度的;console2执行语句只会去判断id这个索引上,有没有5这个锁;
接下来我们反过来
//console1
start transaction ;
select * from simple where id>5 lock in share mode ;
select * from performance_schema.data_locks;
//console2
都会被阻塞
insert into simple (id,name,type,seq) value (6,5,5,5);
insert into simple (id,name,type,seq) value (36,5,5,5);你试着一起敲一下就会发现,咦,console2怎么阻塞了呢?按上面所说的,不是不应该吗?
实际上console1的执行锁的确实是id;
但是你console2的执行,会回表啊,会尝试给id加写锁,但是id已经加了读锁了,所以自然不行了;
所以,不要盲目的只看查询条件,要理解当前语句都会加什么锁,是否和已经加的锁冲突;
最后,我们再来看一个附加题,下面两个语句加的锁是否一样呢?
start transaction ;
select * from simple where id>=5 lock in share mode ;
select * from performance_schema.data_locks;在我没有尝试之前,我理解都没有回表,那么就应该一个是唯一索引加读锁,一个是唯一索引加写锁;
但是实际结果却是lock in share mode是对的,for update会认为你要更新语句,自动给主键加锁了

case6(next-key lock 和index)

吸取uni的教训,我给seq的值都加了200,现在这个表是这样的

seq=215

start transaction ;
select * from simple where id>5 and id<20 lock in share mode ;
select * from performance_schema.data_locks;
除了意向锁,其他三个我们一个个看;
seqidx(S)这行是普通索引执行时加的临键锁,由于不是唯一索引,所以不能优化(因为可能存在重复)
primary(S,REC_NOT_GAP)这是回表操作带来的
seqidx(S,GAP)这行是因为不是唯一索引,所以在查询到匹配的值之后不会立马停止(因为后面可能还存在相同的值),所以必须要到不符合条件的值为止,而所有查询过的都会加索引,所以存在一个间隙锁。
seq=216

start transaction ;
select * from simple where uni = 15 lock in share mode ;
select * from performance_schema.data_locks;
我理解,应该是从205开始查,查到第一个不符合条件的值是215,加上中间没有回表,所以就这一个锁;理论应该是(215,220],但由于优化2,所以退化为间隙锁;
seq>215 and seq215 and seq 215 and seq 215 and seq 230和前面unidx>130和id>30都一样
case7(next-key和没有索引)

alter table simple drop indexseqidx;start transaction ;
select * from simple where id>5 and id<20 lock in share mode ;
select * from performance_schema.data_locks;前面提到过,查询条件匹配不到索引或者只是索引的一部分,这个时候为了保证数据的准确性,会给整个表“加锁”,其实给表里所有的记录都加锁(这里我不知道描述的对不对,因为表锁!=所有记录加锁,虽然效果相似,但并不是一个东西).


同时因为这个表存在意向读锁,通过lock tables simple write 加写的表锁会冲突;
参考文档:

06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?-极客时间
mysql MDL读写锁阻塞,以及online ddl造成的“插队”现象_花落的速度的博客-CSDN博客

来源:https://www.cnblogs.com/qisi/p/innodb_lock_case.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 锁(case篇)