【MySQL进阶之路 | 高级篇】全局锁与死锁

1. 全局锁

全局锁就是对整个数据库实例加锁。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。全局锁的典型使用场景是:做全库逻辑备份。

全局锁的命令:

flush tables with read lock;

2. 死锁

2.1 概念

两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会主动释放自己的锁。

2.2 死锁产生的必要条件

  1. 两个或两个以上的事务。
  2. 每个事务都已经持有锁并且申请新的锁。
  3. 锁资源同时只能被同一事务持有或者不兼容。
  4. 事务之间因为持有锁和申请锁导致彼此循环等待。

死锁的关键在于:两个或两个以上的Session加锁的顺序不一样。

2.3 如何处理死锁

  • 方式1:等待,直到超时(innodb_lock_wait_timeout=50s)。

即当两个事务互相等待时,当一个事务等待时间超过设置的阈值时,就将其回滚,另外事务继续进行。这种方法简单有效,在innodb中,参数innodb_lock_wait_timeout用来设置超时时间。

缺点:对于在线服务来说,这个等待时间往往是无法接受的。

那将此值修改短一些,比如1s,0.1s是否合适?不合适,容易误伤到普通的锁等待。

  • 方式2:使用死锁检测进行死锁处理

方式1检测死锁太过被动,innodb还提供了wait-for graph算法来主动进行死锁检测,每当加锁请求无法立即满足需要并进入等待时,wait-for graph算法都会被触发。

这是一种较为主动的死锁检测机制,要求数据库保存锁的信息链表和事务等待链表两部分信息。

基于这两个信息,可以绘制wait-for graph(等待图)

死锁的检测原理就是构建一个以事务为顶点,锁为边的有向图。判断有向图是否存在环,存在即有死锁。

一旦检测到回路、有死锁,这时候InnoDB存储引擎会选择`回滚undo量最小的事务`,让其他事务继续执行( innodb_deadlock_detect=on表示开启这个逻辑)。

缺点:每个新的被阻塞的线程,都要判断是不是由于自己的加入导致了死锁,这个操作时间复杂度是o(n)。如果100个并发线程同时更新同一行,意味着要检测100*100= 1万次,1万个线程就会有1千万次检测。

如何解决:

  • 方式1:关闭死锁检测,但意味着可能会出现大量的超时,会导致业务有损。
  • 方式2:控制并发访问的数量。比如在中间件中实现对于相同行的更新,在进入引擎之前排队,这样在InnoDB内部就不会有大量的死锁检测工作.

2.4 如何避免死锁

  • 合理设计索引,使业务sQL尽可能通过索引定位更少的行,减少锁竞争。
  • 调整业务逻辑SQL执行顺序,避免update/delete长时间持有锁的sQL在事务前面。
  • 避免大事务,尽量将大事务拆成多个小事务来处理,小事务缩短锁定资源的时间,发生锁冲突的几率也更小。
  • 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  • 在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。如select ... for update 语句,如果是事务里运行了start transaction或设置了autocommit等于o,那么就会锁定所查找到的记录。
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值