• mysql隔离级别RR下的行锁、临键锁、间隙锁详解及运用


    一:mysql 锁的基本概念

    锁:悲观锁、乐观锁

    悲观锁:写锁 for update、读锁for share 

    写锁:只允许当前事务读写,其它事务全部等待,包括读取数据,锁的数据范围需要具体分析

    读锁:允许所有事务进行读取,但不允许写,首先获得数据读锁的事务可以在事务中进行写操作,一旦进行了写操作,其它事务就会全部进行等待,包括读也不允许了,所以一般需要读又要写的使用“写锁”

    二:加锁数据范围规则

            原则1:访问到的数据都要加"临键锁"
            原则2:查询时会在索引上访问到第一个不满足需要查询数据的值为止,不满足条件的第一个值上也会加"临键锁"
            原则3:索引查询(除唯一索引等值{查到了值}查询和非唯一索引范围查询以外)都加 "临键锁退化为间隙锁"
            原则4:唯一索引等值查询{查到了值}退化为"行锁"

            bug: 唯一索引范围查询时依旧会往下一个值查询,且不会退化为间隙锁,依旧是左开右闭;不过有的mysql版本是左开又开(间隙锁);范围查询有等于的时候不会向下一个滑寻找不等于自己的值;如果存在Bug的版本就和非唯一索引的结果一样,都是临建锁+下滑;如果修复了bug那就是间隙锁    {仅只针对唯一索引范围查询存在的Bug}

    结论:

            理论上: 

            当前测试的mysql版本:

               结果:

            1.唯一索引和非唯一索引(除了唯一索引等值查询)都会向右遍历且最后一个值不满足等值条件的时候才停止。

            2.唯一索引和非唯一索引“范围查询”都不会退化为间隙锁,非唯一索引本身就不会退化,唯一索引是因为mysql版本的bug原因不退化 ==》     范围查询存在BUG就会下滑且都加临建锁

            3.非唯一索引和唯一索引等值查询(未差查到值) 都是会下滑且都会优化为间隙锁

    可以通过  select * from performance_schema.data_locks; 检验

    测试数据

    一:索引

            1、非唯一索引

            2、唯一索引

    二:非唯一索引

    select * from  cs where age=20  for update
    -- 非唯一索引等值查询{查到了值}
    -- 首先给20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找首个不等于20的值为止,也就是30,给30上临键锁,范围(20,30],又根据原则3退化为间隙锁范围(20,30)
    -- 最终范围(10,30)

    select * from  cs where age=21  for update
    -- 非唯一索引等值查询{没查到值}
    -- 首先21的值没查到,没查到的值不用上临键锁
    -- 根据原则2会去寻找首个不等于21的值为止,也就是30,给30上临键锁,范围(20,30],又根据原则3退化为间隙锁范围(20,30)
    -- 最终范围(20,30)

    select * from  cs where age=2000  for update
    -- 非唯一索引等值查询{没查到值}
    -- 首先2000的值没查到,并且是超出了表的已有数据范围之外的数据,可以自己默认上个临键锁(100,2000]
    -- 根据原则2会去寻找首个不等于2000的值为止,但此时没有符合条件的,此时就会一直找+∞,所以会锁定(2000,+∞]的数据
    -- 最终范围(100,+∞]

    select * from  cs where age>20 for update
    -- 非唯一索引范围查询
    -- 首先>20是属于20-30之间的一个不确定值(不含20),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找首个不等于这个大于20的值为止,也就是30,给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 又因为是>20没有后部分的区间,所以会加锁(30,+∞]
    -- 最终范围(20,+∞]


    select * from  cs where age>=20 for update
    -- 非唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找首个不等于这个等于20的值为止,也就是30,给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 又因为是>=20没有后部分的区间,所以会加锁(30,+∞]
    -- 最终范围(10,+∞]

    select * from  cs where age>20 and age<22 for update
    -- 非唯一索引范围查询
    -- 首先>20是第一范围区间是在20-30之间的一个不确定值(不含20),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个大于20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 再处理 <22,也是第一范围区间是在20-30之间的一个不确定值(不含230)
    -- 根据原则2会去寻找下一个首个不等于这个小于22的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 最终范围(20,30]


    select * from  cs where age>20 and age<30 for update
    -- 非唯一索引范围查询
    -- 首先>20是第一范围区间是在20-30之间的一个不确定值(不含20),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个大于20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 再处理 <30,也是第一范围区间是在20-30之间的一个不确定值(不含30)
    -- 根据原则2会去寻找下一个首个不等于这个小于30的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 最终范围(20,30]


    select * from  cs where age>20 and age<=30 for update
    -- 非唯一索引范围查询
    -- 首先>20是第一范围区间是在20-30之间的一个不确定值(不含20),也就是范围内不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个大于20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 再处理 <=30,30上临键锁,锁范围(20,30]
    -- 根据原则2会去寻找下一个首个不等于这个30的值为止,也就是35,给35上临键锁,范围(30,35],由于是范围查询不会退化
    -- 最终范围(20,35]


    select * from  cs where age>=20 and age<30 for update
    -- 非唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找下一个首个不等于这个20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 再处理 <30,也是第一范围区间是在20-30之间的一个不确定值(不含30),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个小于30的值为止,也就是30,给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 最终范围(10,30]

    select * from  cs where age>=20 and age<=30 for update
    -- 非唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找下一个首个不等于这个20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 再处理 <=30,30上临键锁,锁范围(20,30]
    -- 根据原则2会去寻找下一个首个不等于这个30的值为止,也就是35,给35上临键锁,范围(30,35],由于是范围查询不会退化
    -- 最终范围(10,35]


    select * from  cs where age>=20 and age<=100 for update
    -- 非唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找下一个首个不等于这个20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化
    -- 再处理 <=100,100上临键锁,锁范围(42,100]
    -- 根据原则2会去寻找下一个首个不等于这个100的值为止,但100后面没有值了,此时就锁(100,+∞]
    -- 最终范围(10,+∞]


    select * from  cs where age>=1 and age<=1000 for update
    -- 非唯一索引范围查询
    -- 首先>=1,1上临键锁,锁范围(-∞,1]
    -- 根据原则2会去寻找下一个首个不等于这个1的值为止,也就是10,优先暂时给10上临键锁,范围(1,10],由于是范围查询不会退化
    -- 再处理 <=1000,1000上临键锁,锁范围(42,100]
    -- 根据原则2会去寻找下一个首个不等于这个1000的值为止,但此时没有符合条件的,此时就会一直找+∞,所以会锁定(1000,+∞]的数据
    -- 由于是范围查询,根据原则1被查询的到的数据都会加锁,所以1-100的数据都会加临键锁
    -- 最终范围[-∞,+∞]


    select * from  cs where age>=1 and age<100 for update
    -- 非唯一索引范围查询
    -- 首先>=1,1上临键锁,锁范围(-∞,1]
    -- 根据原则2会去寻找下一个首个不等于这个1的值为止,也就是10,优先暂时给10上临键锁,范围(1,10],由于是范围查询不会退化
    -- 再处理 <100,也是第一范围区间是在42-100之间的一个不确定值(不含100),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个小于100的值为止,也就是100,给100上临键锁,范围(42,100],由于是范围查询不会退化
    -- 由于是范围查询,根据原则1被查询的到的数据都会加锁,所以1-100的数据都会加临键锁
    -- 最终范围[-∞,100]

    二:唯一索引  【范围中带等于的是因为某些版本的bug会下滑,不是所有版本都会下滑,有些版本会退化为行锁,临键锁也会退化为间隙锁】

    select * from  cs where wy=20  for update
    -- 唯一索引等值查询{查到了值}
    -- 首先给20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找首个不等于20的值为止,也就是30,给30上临键锁,范围(20,30],又根据原则3退化为间隙锁范围(20,30)

    -- 根据原则4所有的锁都将退化为行锁
    -- 最终范围 20

    select * from  cs where wy=21  for update
    -- 唯一索引等值查询{没查到值}
    -- 首先21的值没查到,没查到的值不用上临键锁
    -- 根据原则2会去寻找首个不等于21的值为止,也就是30,给30上临键锁,范围(20,30],又根据原则3退化为间隙锁范围(20,30)
    -- 最终范围(20,30)

    select * from  cs where wy=2000  for update
    -- 唯一索引等值查询{没查到值}
    -- 首先2000的值没查到,并且是超出了表的已有数据范围之外的数据,可以自己默认上个临键锁(100,2000]
    -- 根据原则2会去寻找首个不等于2000的值为止,但此时没有符合条件的,此时就会一直找+∞,所以会锁定(2000,+∞]的数据

    --但因为是唯一索引,如果超出数据范围外就不会再上锁
    -- 最终范围    无锁

    select * from  cs where wy>20 for update
    -- 唯一索引范围查询
    -- 首先>20是属于20-30之间的一个不确定值(不含20),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找首个不等于这个大于20的值为止,也就是30,给30上临键锁,范围(20,30],

    -- 根据原则3会退化为间隙锁(20,30)  这里会不会退化有带考证,可能会因为Bug不退化
    -- 又因为是>20没有后部分的区间,所以会加锁(30,+∞]
    -- 最终范围(20,+∞]


    select * from  cs where wy>=20 for update
    -- 唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20],根据原则3会退化为间隙锁(10,20) 这里会不会退化有带考证,可能会因为Bug不退化,根据原则4还会上行锁 20    所以最终(10,20]
    -- 根据原则2会去寻找首个不等于这个等于20的值为止,也就是30,给30上临键锁,范围(20,30] 

    -- 根据原则3会退化为间隙锁(20,30)  这里会不会退化有带考证,可能会因为Bug不退化
    -- 又因为是>=20没有后部分的区间,所以会加锁(30,+∞]
    -- 最终范围(10,+∞]     当然还有对应的主键id锁

    select * from  cs where wy>20 and wy<22 for update        
    -- 唯一索引范围查询
    -- 首先>20是第一范围区间是在20-30之间的一个不确定值(不含20),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个大于20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)   但因为bug,还是(20,30]
    -- 再处理 <22,也是第一范围区间是在20-30之间的一个不确定值(不含30)
    -- 根据原则2会去寻找下一个首个不等于这个小于22的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)   但因为bug,还是(20,30]
    -- 最终范围(20,30]        因为bug根据原则2会锁住下一个值的临建锁且不会退化


    select * from  cs where wy>20 and wy<30 for update 
    -- 唯一索引范围查询
    -- 首先>20是第一范围区间是在20-30之间的一个不确定值(不含20),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个大于20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)   但因为bug,还是(20,30]
    -- 再处理 <30,也是第一范围区间是在20-30之间的一个不确定值(不含30)
    -- 根据原则2会去寻找下一个首个不等于这个小于30的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)  但因为bug,还是(20,30]
    -- 最终范围(20,30]


    select * from  cs where wy>20 and wy<=30 for update   
    -- 唯一索引范围查询
    -- 首先>20是第一范围区间是在20-30之间的一个不确定值(不含20),也就是范围内不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个大于20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)   但因为bug,还是(20,30]
    -- 再处理 <=30,30上临键锁,锁范围(20,30]
    -- 根据原则2会去寻找下一个首个不等于这个30的值为止,也就是35,给35上临键锁,范围(30,35],根据原则3会退化为间隙锁(30,35)   但因为bug,还是(20,35]
    -- 最终范围(20,35]


    select * from  cs where wy>=20 and wy<30 for update     
    -- 唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20]  
    -- 根据原则2会去寻找下一个首个不等于这个20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)

    --根据原则4退化为行锁 最终锁定行锁   20     但因为bug,还是(20,30]
    -- 再处理 <30,也是第一范围区间是在20-30之间的一个不确定值(不含30),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个小于30的值为止,也就是30,给30上临键锁,范围(20,30],根据原则3会退化为间隙锁(20,30)   但因为bug,还是(20,30]
    -- 最终范围(10,30]

    select * from  cs where wy>=20 and wy<=30 for update     
    -- 唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20] 
    -- 根据原则2会去寻找下一个首个不等于这个20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],由于是范围查询不会退化   但因为bug,还是(20,30]
    -- 再处理 <=30,30上临键锁,锁范围(20,30]
    -- 根据原则2会去寻找下一个首个不等于这个30的值为止,也就是35,给35上临键锁,范围(30,35],根据原则3会退化为间隙锁(30,35)    但因为bug,还是(30,35]
    -- 最终范围(10,35]


    select * from  cs where wy>=20 and wy<=100 for update  
    -- 唯一索引范围查询
    -- 首先>=20,20上临键锁,锁范围(10,20]
    -- 根据原则2会去寻找下一个首个不等于这个20的值为止,也就是30,优先暂时给30上临键锁,范围(20,30],  但因为bug,还是(20,30]
    -- 再处理 <=100,100上临键锁,锁范围(42,100]
    -- 根据原则2会去寻找下一个首个不等于这个100的值为止,但100后面没有值了,此时就锁(100,+∞]
    -- 最终范围(10,+∞]


    select * from  cs where wy>=1 and wy<=1000 for update  
    -- 唯一索引范围查询
    -- 首先>=1,1上临键锁,锁范围(-∞,1]
    -- 根据原则2会去寻找下一个首个不等于这个1的值为止,也就是10,优先暂时给10上临键锁,范围(1,10],  但因为bug,还是(1,10]
    -- 再处理 <=1000,1000上临键锁,锁范围(42,100]
    -- 根据原则2会去寻找下一个首个不等于这个1000的值为止,但此时没有符合条件的,此时就会一直找+∞,所以会锁定(1000,+∞]的数据
    -- 由于是范围查询,根据原则1被查询的到的数据都会加锁,所以1-100的数据都会加临键锁
    -- 最终范围[-∞,+∞]


    select * from  cs where wy>=1 and wy<100 for update   
    -- 唯一索引范围查询
    -- 首先>=1,1上临键锁,锁范围(-∞,1]
    -- 根据原则2会去寻找下一个首个不等于这个1的值为止,也就是10,优先暂时给10上临键锁,范围(1,10],  但因为bug,还是(1,10]
    -- 再处理 <100,也是第一范围区间是在42-100之间的一个不确定值(不含100),也就是不存在的值,没查到的值不用上临键锁
    -- 根据原则2会去寻找下一个首个不等于这个小于100的值为止,也就是100,给100上临键锁,范围(42,100],  但因为bug,还是(1,10]
    -- 由于是范围查询,根据原则1被查询的到的数据都会加锁,所以1-100的数据都会加临键锁
    -- 最终范围[-∞,100]

                    

  • 相关阅读:
    Tomcat中GET和POST请求时乱码解决
    WorkPress使用BackWPup插件备份后手动还原方法记录
    Outlook屏蔽Jira AI提醒
    腾讯云年终选购云服务器攻略!
    全网最详细教程(上):教你如何从0-1制作出一张可视化大屏
    专利终止怎么恢复 ?
    苹果笔记本充不了电的解决办法
    红日ATT&CK系列靶场(一)
    视频监控与视频编解码技术
    Layui自定义列表多选
  • 原文地址:https://blog.csdn.net/qq_35572020/article/details/127629464