mysql 第8.17章 事务-死锁 mysql 第8.17章 事务-死锁

59分钟前

一、问题

数据库的死锁是怎么产生的?两个事务抢锁,谁也不让谁就产生死锁了。

出现死锁了怎么排查和解决?

二、死锁的原理

死锁的原理特别好理解,比如:你和朋友去吃饭,你拿了筷子,他拿了,你要等他的碗才能吃饭,他要等你筷子才能吃饭,你们两个谁也不松手,然后就僵在这里了,这个就是死锁。

对应到数据库里面,必须同时满足 4 个条件才会出现死锁:

  1. 互斥条件:一个锁同一时间只能被一个事务持有,就像筷子同一时间只能由一个人去用

  2. 持有并等待:我已经拿了一个锁,还要等着拿另一个锁,手里的锁呢我还不松手

  3. 不可剥夺:别人拿的锁我不能硬抢,只能等别人自己释放

  4. 循环等待:a 等 b 的锁,b 等 a 的锁,形成了一个死循环

这 4 个条件同时满足就形成了死锁。

三、死锁的定位

3.1、SQL

show engine innodb status

这个语句会清楚的告诉你哪两个事务产生了死锁,这个两个事务分别执行了什么 SQL,抢的是什么类型的锁,最后数据库自动回滚了哪一个事务

3.2、业务代码找问题

拿着死锁的日志里面的两个 SQL 语句,去找对应的业务代码。

90% 的情况都是事务执行顺序反了:比如说事务 1 先改订单表,再改用户表;事务 2 先改用户表,再改订单表;刚好形成一个循环等待

3.3、检查不必要锁

看是不是锁的粒度太大了:比如明明应该加行锁的,结果因为字段没有加索引,导致了锁所有的行。

普通查询加了一些没必要的悲观锁:比说 for update 导致锁的范围变大了,更容易产生冲突

四、死锁的解决

4.1、统一事务执行顺序

所有涉及到多表修改的事务, 都按同一个顺序来执行。

比如说所有改订单和用户的表的事务,都先改订单再改用户,大家按照同一个的顺序去拿锁,就不会出现循环等待。

4.2、大事务拆成小事务

锁的持有时间越短,冲突的概率也就越低。

不要在事务里面去,加 RPC 的调用、去查 Redis,不要去处理业务逻辑这些无关的操作,尽量的让事务只做数据库的操作。

并且SQL不要去处理复杂的业务逻辑,把业务逻辑下沉到代码里面去做,快点执行完提交释放锁

4.3、降低锁的粒度

能用行锁就不要去用表锁。

能用乐观锁就不要去用悲观锁,比如说你可以用版本号实现乐观锁,不用去用 for update 去加悲观锁。

锁的范围越小,冲突的概率也就越低。

你在改数据的时候,一定要基于索引去改。

4.4、设置锁超时时间兜底

MySQL 里面有一个 lock_wait_timeout 的参数,可以设置锁的等待时间。

超过时间它会自动回滚其中一个事务,让另外一个事务能正常地执行,不会一直僵住导致业务不可用。

五、死锁的发送概率

死锁其实很少发生:

大部分都是因为代码写的不规范。

事务执行的顺序乱了导致的,只要统一了执行顺序,基本上就能避免 99% 的死锁问题。

阅读 4

mysql文章
带到手机上看