一文讲不清mysql里的加锁机制

首先你要知道:仅仅一篇文章是无法全面讲清楚明白mysql里面的加锁机制的!

虽然网上有很多文章都号称“理解mysql里的加锁机制看这一篇就够了”,实际上都只是理论方面到位,工程实践方面则都所不足。

本文针对工程实践方面经常会碰到的一些关于锁的问题进行剖析:

1.锁的类型:

经常碰到的主要是行锁(record lock),间隙锁(gap lock)和(next-key 锁)。

  • 行锁表示锁住了某一行记录
  • 间隙锁表示锁定了一个区间,在这个区间不能新增记录。在一个区间不能新增记录就避免了所谓幻读的问题,叫区间锁好像更好听点,所以下面就叫区间锁好了。注意:区间锁不会对区间两边的记录加锁,只对区间内部加锁。
  • next-key锁可以看成是以上两者的结合,表示既对记录加了行锁又在记录的前面加上了区间锁,实际上底层也是这样实现的。

举个例子:

select * from t where id=100 for update
或者
update t set name=? where id=100

上面两种类型的sql语句的加锁方式是一致的:

  • 如果id为唯一索引,100这条记录存在,则会对这条记录加上行锁。
  • 如果id为唯一或非唯一索引,100这条记录不存在,则会加上区间锁,具体的区间为小于100的第一条记录到大于100的第一条记录之间的区间。
  • 如果id是非唯一索引,100这条记录存在,则会加上next-key lock,既锁定100这条记录,又锁定小于100的第一条记录到100之间的区间。

另外:如果查询条件是大于或小于某个值,也即查询的结果预期是一个区间时也会加区间锁。

我们这可以这样来分析:

  1. 如果查询条件命中了一个区间则会加上区间锁
  2. 如果只是精确命中了记录则给记录加上行锁(前提条件:查询条件为唯一索引)
  3. 区间锁是为了防止加锁期间其他事务在区间新增记录而设计的,所以,如果查询条件是非唯一索引时且命中记录时会加next-key锁,它相当于包含了一个区间锁:锁定命中的记录和记录之前的区间。有了区间锁,其他事务就不能在区间内新增记录从而产生幻读了。

2.共享和排他锁:

共享锁就不讲了,平时常用的主要是排他锁,即一个会话对某行记录加了排他锁,则其他会话就不能再获得这把锁。

但是在mysql里面:区间锁相互之间是不互斥的,也就是不同会话可以同时给同一个区间加锁!

这样一来,就会产生以下几种情况:

  • 如果对方获得了行锁,则你只能等它释放。
  • 如果对方获得了区间锁,则你也可以获取相同的区间锁。
  • 如果对方获得了next-key锁,则表示对方获得了行锁和区间锁,这时你不能获取对方持有的行锁,但可以获取对方持有的区间锁。

所以,要不要等待对方的锁,就看你当前申请的锁是否包含对方持有的行锁,如果有则需要等待,否则不需要。

3.死锁问题

一般来说,如果所有事务都保持一致的加锁顺序,则不容易产生死锁。但是不容易产生,不代表不会产生,在一些特殊的情况下还是有可能碰到的!

这是因为在mysql中有这样一个奇葩的机制:

一个会话在申请锁的时候如果需要等待另一方释放锁,则这个申请会进入锁申请队列,这时如果另一方接下来需要申请的锁与申请队列中的锁有冲突,则会产生死锁!

比如有这样两个会话:

会话1:

select * from t where id=100 for update //id是唯一索引且记录存在,这里产生行锁

会话2:

select * from t where id=100 for update //id是唯一索引且记录存在,因为会话1持有行锁,会话2只能等待,该申请会进入锁申请队列。

这时如果会话1再执行:

update t set name=xxx where id>90

此处查询条件的结果是一个区间,于是产生了区间锁,这个锁与申请队列中的会话2申请的锁有冲突,就产生的死锁!

是不是很奇葩?会话2还没有持有任何锁,仅仅是申请了锁而已,就产生了死锁。mysql大概是这样判断的:

  1. 会话1申请申请区间锁。
  2. 因为队列里有其他申请,于是先跟其他申请做下比较,发现会话2已经先申请了id=100这条记录的行锁且处于区间中,所以它认为会话1申请在后需要等待会话2。
  3. 这时为避免死锁再去检查会话2申请的锁是否被会话1持有
  4. 不检查不知道,一检查吓一跳:会话1持有会话2需要的锁,而在第二步时会话1当前又需要等待会话1!
  5. 结论:有死锁,选择牺牲会话2!

 

转载于:https://my.oschina.net/u/150599/blog/3102642

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值