文章目录
- 引言
- 正文
- 什么SQL语句会加行级锁
- 查询操作增加对应的行级锁
- 事务的写法
- update和delete修改操作也会增加行级锁
- 行级锁有哪些种类
- 记录锁
- 间隙锁
- Next-Key锁
- MySQL是如何加行级锁?
- 唯一索引等值查询
- 查询记录是存在的
- 查询记录是不存在的
- 唯一索引范围查找
- 针对大于或者大于等于的范围查询
- 针对小于等于的范围查询
- 非唯一索引等值查询
- 记录不存的情况
- 记录存在的时候
- 非唯一 索引范围查询
- 没有加索引的查询
- 总结
引言
- 在做一些SQL的过程中,总是对于行级锁会出现的一些理解上的问题,就找了一些资料,来补充一下,之前只不过是背过了,但是并不深刻理解。
- 这里参考的都是小林coding的内容,链接
- 这里是看了小林coding的内容,然后自己在进行二次消化总结,目录结构基本上是一致的。
正文
什么SQL语句会加行级锁
-
普通的select语句不加行级锁,快照读,通过MVCC来实现的。
-
两种类型的锁,分别是共享锁和独占锁
- 共享锁S:满足读读共享,读写互斥。
- 独占锁X:满足写写互斥,读写互斥。
查询操作增加对应的行级锁
查询加锁的方式有两种,枷锁的查询语句成为锁定读
S型锁
select ... lock in share mode;
X型锁
select ... for update;
注意
- 加锁的读语句,必须要在一个事务中,因为事务结束了就会自动释放锁。
事务的写法
事务的书写方式!!!这里个东西吧,拼多多就问了,就是会问你实操!
使用start transaction
START TRANSACTION; // 开启一个新事物
//..///不同的SQL语句
selet * from table_a where id = 1 for update;
COMMIT;// 最后事务要提交
使用begin实现事务
Begin; // 开启一个新事物
//..///不同的SQL语句
selet * from table_a where id = 1 for update;
COMMIT;// 最后事务要提交
update和delete修改操作也会增加行级锁
- 删除和修改操作增加的都是独占型锁X型锁
update table colA = x where id = 1;
delete from table where id = 1;
行级锁有哪些种类
-
记录锁Record Lock
- 紧紧锁住一条记录
-
间隙锁Gap Lock
- 锁定一个范围,但是不包含记录本身
-
Next-key锁
- 记录锁和间隙锁的组合,锁定一个范围并且锁定记录本身。
不同隔离级别下,锁是不同的
- 读已提交的情况下,是记录锁
- 可重复读的情况下,是记录锁和间隙锁(防止幻读)
记录锁
- 记录锁,同时有共享锁和独占锁,需要自己指定。
- 默认是独占锁,所以不能对这条记录进行任何操作
begin;
select * from table where condition for update;
commit;
间隙锁
-
用于可重复读隔离级别,是解决幻读问题。
-
在一定范围内,不再插入其他的数据
-
间隙锁之间是相互兼容的。
-
不包含自身
Next-Key锁
- 是记录锁和间隙锁的结合体,锁住一个范围的同时,还锁住了自身。
- 保护该记录,并且组织其他事务将新纪录插入到被保护的记录前面的间隙中。
不同Next-Key锁之间是互不兼容的
MySQL是如何加行级锁?
- 加锁的对象是索引,加锁的基本单位是next-key锁,
- 但是在能够使用记录锁或者间隙锁就能避免幻读的场景
- next-key锁会退化为记录锁和间隙锁
加锁的对象是针对索引的!!!
唯一索引等值查询
- 使用唯一索引,进行等值查询的时候,按照查询记录是否存在,将锁退化为两种不同的形态
查询记录是存在的
- 索引树上定位到这条记录后,将记录中的next-key退化为记录锁
陷入阻塞
退化为记录锁的原因
- 主键具有唯一性,插入一个同主键的key-value,不满足唯一性约束。
- 加了记录锁,就无法删除该纪录。
查询记录是不存在的
- 在查询树上找到第一条大于该查询记录的记录后,将该记录中的next-key锁退化为间隙锁。
确定间隙锁的范围
- 大于当前搜索的id的最接近的一个值,以当前值右边界,然后前一个值就是左边界。
- 在唯一索引等值擦查询并且记录不存在的情况下,在索引树找到第一条大于该查询记录的记录后,将该记录的索引next-key退化为间隙锁
使用间隙锁,保证右边界不会被锁,在限定范围内就可以保证不出现幻读
唯一索引范围查找
唯一索引范围查找过程
- 会对每一个扫描到的索引,加上next-key锁,然后如果遇到下述情况,会退化成记录锁或者间隙锁。
针对大于或者大于等于的范围查询
大于的情况
- 对两个不同的区域加上next-key锁,限定访问范围
- 第一个匹配的主键索引上,增加(edge,value]的范围锁,限定在这个范围内不在增加新的数据。
- 对于最后一个特殊暴击的主键的索引上,就增加一个(value,+∞】主键索引,限定在这个范围内,不在增加的新的数据。
大于等于的情况
- 在等值索引匹配上,增加了一个记录锁,限定当前的值不被修改。其余都是相同的。
总结 - 针对大于等于条件的唯一索引范围查询的情况下,如果条件值得记录存在于表中,该查询是等值擦汗寻,所以该记录锁对应得索引中锁退化为记录锁
针对小于等于的范围查询
-
第一个数据增加的是next-key锁,限定是(-∞,a]
- 保证不能在a之前插入数据
-
后续每一个索引,加的锁都是next-key锁,限定范围是(left_edge,right-edge】
- 保证上一个匹配到当前匹配之间的范围内,不增加任何新的数据
-
找到第一个不满足约束条件的索引,当前退化为间隙锁
- 保证在最后一个匹配的样本,到第一个不匹配的样本的范围内,不会插入新的元素
非唯一索引等值查询
- 当我们使用非唯一索引进行等值查询的时候,存在两个索引,一个主键索引,还有一个是非唯一索引
- 加锁的时候,会同时对两个索引都加锁
- 对主键索引加锁的时候,只有对满足查询条件的记录,才会对主键索引进行加锁。
查询记录存在并且使用的不是唯一索引
非唯一索引等值查询过程
- 对整个表格进行扫描,直到扫描到第一个不符合条件条件的二级索引记录,就会停止扫描
- 再扫描过程中,对于的扫描到的二级索引记录增加的是next-key锁,
- 对于第一个不符合条件的二级索引记录,该二级索引的next-key锁退化为间隙锁,
- 对于符合查询条件的记录的主键索引加上记录锁。
记录不存的情况
- 首先会对非唯一索引进行排序,找到第一个不满足等值条件的记录,
- 二级索引,退化为间隙锁。
- 二级索引,退化为间隙锁。
- 能不能插入成功,是需要考虑到二级索引底层结构的实现,首先对二级索引进行排序,然后再具有相同二级索引的记录的主键索引进行排序。
记录存在的时候
- 加锁过程具体如下
- 符合条件的记录的主键索引退化成记录锁
- 二级索引当前锁保持不变
- 第一个不满足条件的二级索引,退化为间隙锁。
非唯一 索引范围查询
- 非唯一索引查询,索引的nextkey 不会退化成间隙锁和记录锁的情况,二级索引加的也是next-key锁
- 满足条件的主键索引仍旧是记录锁
没有加索引的查询
- 如果锁定读查询语句,没有使用索引列座位查询条件,或者查询语句没有走索引查询,导致扫描是全表扫描的
- 每一条记录的索引上,都会加next-key锁,锁住全表
不走索引,只要是执行update、delete、for update都是锁住整个表
总结
- 想想就难受呀,拼多多主管面,就剩下最后一面了,那个主管面,基本上什么都没答出来,很难受,但凡那里看了一遍,我都不至于面试面的那么差,而且很多东西都很基础,我就是忘记了。没注意到这些细节,很难受!
- 不想了,技不如人,没有准备好,那几天太浪了,没有好好备战,活该吧!后续提前批,要加把劲!
- 事情蛮多的,这里太细了,不过大概懂了就行了。今晚还得赶着刷两道题,还得写一个党政结课的论文。
所有的加锁还有所退化,都是的为了避免幻读!!
当前读一定要使用索引进行操作,否则就是全表锁住