mysql锁

锁的分类

数据库里有的锁有很多种,分别如下:

基于锁的属性分类:共享锁、排他锁

基于锁的粒度分类:表锁、行锁(记录锁、间隙锁、临键锁)

基于锁的状态分类:意向共享锁、意向排它锁

状态锁包括意向共享锁和意向排它锁,表示是否可以对某一个表进行加表锁的状态

意向锁的解释:当一个事务试图对整个表进行加锁(共享锁或排它锁)之前,首先需要获得对应类型的意向锁(意向共享锁或意向共享锁)。意向锁能有效减少检查各个页或行锁操作,而只需检查表上的意向锁。意向锁是有数据引擎自己维护的,用户无法手动操作意向锁。

意向共享锁: 事务有意向对表中的某些行加共享锁(S锁)

意向排他锁: 事务有意向对表中的某些行加排他锁(X锁)

这里的S锁和X锁是表级别,意向锁不会与行级的共享 / 排他锁互斥

可参考:美团一面:能不能通俗的解释下为什么要有意向锁这个东西?-51CTO.COM

mysql innodb的锁是通过锁索引来实现的,select for update排它锁举例,

  • 如果字段没有索引,即使使用wehre条件也会进行表级锁。
  • 如果有索引,会锁定对应where条件中索引值的所有行,可理解为对该索引值进行了索引(所以即使另一事务查询的是其他行,但因为使用相同的索引键也会被锁住。)
  • 如果有唯一索引,只会锁一行数据,可以理解为真正的数据行锁。

1. 原则1: 加锁的基本单位是next-keylock。 next-keylock是前开后闭区间。

2. 原则2: 查找过程中访问到的对象才会加锁。

3. 优化1: 索引上的等值查询, 给唯一索引加锁的时候, next-keylock退化为行锁

4. 优化2: 索引上的等值查询, 向右遍历时且最后一个值不满足等值条件的时候, next-key lock退化为间隙锁。

5. 一个bug: 唯一索引上的范围查询会访问到不满足条件的第一个值为止。

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

等值查询间隙锁

1. 根据原则1, 加锁单位是next-keylock, session A加锁范围就是(5,10];

2. 同时根据优化2, 这是一个等值查询(id=7), 而id=10不满足查询条件, next-keylock退化成间隙锁, 因此最终加锁的范围是(5,10)。

所以, session B要往这个间隙里面插入id=8的记录会被锁住, 但是session C修改id=10这行是可以的。

非唯一索引等值锁

1. 根据原则1, 加锁单位是next-keylock, 因此会给(0,5]加上next-keylock。

2. 要注意c是普通索引, 因此仅访问c=5这一条记录是不能马上停下来的, 需要向右遍历(普通索引数据可重复,唯一索引不可重复), 查到c=10才放弃。 根据原则2,访问到的都要加锁, 因此要给(5,10]加next-keylock。

3. 但是同时这个符合优化2: 等值判断, 向右遍历, 最后一个值不满足c=5这个等值条件, 因此退化成间隙锁(5,10)。

4. 根据原则2 , 只有访问到的对象才会加锁, 这个查询使用覆盖索引, 并不需要访问主键索

引, 所以主键索引上没有加任何锁, 这就是为什么session B的update语句可以执行完成。

但session C要插入一个(7,7,7)的记录, 就会被session A的间隙锁(5,10)锁住。

需要注意, 在这个例子中, lock in share mode只锁覆盖索引, 但是如果是for update就不一样了。 执行 for update时, 系统会认为你接下来要更新数据, 因此会顺便给主键索引上满足条件的行加上行锁。

主键索引范围锁

1. 开始执行的时候, 要找到第一个id=10的行, 因此本该是next-keylock(5,10]。 根据优化1,主键id上的等值条件, 退化成行锁, 只加了id=10这一行的行锁。

2. 范围查找就往后继续找, 找到id=15这一行停下来, 因此需要加next-keylock(10,15]。所以, session A这时候锁的范围就是主键索引上, 行锁id=10和next-keylock(10,15]。 这样, session B和session C的结果你就能理解了。

这里你需要注意一点, 首次session A定位查找id=10的行的时候, 是当做等值查询来判断的, 而向右扫描到id=15的时候, 用的是范围查询判断。

非唯一索引范围锁

这次session A用字段c来判断, 加锁规则跟案例三唯一的不同是: 在第一次用c=10定位记录的时

候, 索引c上加了(5,10]这个next-keylock后, 由于索引c是非唯一索引, 没有优化规则, 也就是

说不会蜕变为行锁, 因此最终sesion A加的锁是, 索引c上的(5,10] 和(10,15] 这两个next-key

lock。

唯一索引范围锁

session A是一个范围查询, 按照原则1的话, 应该是索引id上只加(10,15]这个next-keylock, 并且因为id是唯一键, 所以循环判断到id=15这一行就应该停止了。

但是实现上, InnoDB会往前扫描到第一个不满足条件的行为止, 也就是id=20。 而且由于这是个范围扫描, 因此索引id上的(15,20]这个next-keylock也会被锁上。

所以你看到了, session B要更新id=20这一行, 是会被锁住的。 同样地, session C要插入id=16的一行, 也会被锁住。

照理说, 这里锁住id=20这一行的行为, 其实是没有必要的。 因为扫描到id=15, 就可以确定不用往后再找了。 但实现上还是这么做了, 因此我认为这是个bug。(《Mysql45讲》)


这时, session A在遍历的时候, 先访问第一个c=10的记录。 同样地, 根据原则1, 这里加的是

(c=5,id=5)到(c=10,id=10)这个next-keylock。然后, session A向右查找, 直到碰到(c=15,id=15)这一行, 循环才结束。 根据优化2, 这是一个等值查询, 向右查找到了不满足条件的行, 所以会退化成(c=10,id=10) 到 (c=15,id=15)的间隙锁。

limit 语句加锁

这个例子里, session A的delete语句加了 limit 2。 你知道表t里c=10的记录其实只有两条, 因此加不加limit 2, 删除的效果都是一样的, 但是加锁的效果却不同。

可以看到, session B的insert语句执行通过了, 跟案例六的结果不同。这是因为, 案例七里的delete语句明确加了limit 2的限制, 因此在遍历到(c=10, id=30)这一行之后,

满足条件的语句已经有两条, 循环就结束了。

在删除数据的时候尽量加limit。 这样不仅可以控制删除数据的条数, 让操作更安全, 还可以减小加锁的范围。

参考文档:

《Mysql45讲》

Mysql 锁详解 锁 与索引的关系

MySQL索引优化

MySQL事务

 详解 MySql InnoDB 中意向锁的作用

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值