MySQL死锁产生的原因和解决方法

前言最近老顾经常碰到同事说,MySQL又死锁了导致业务报错 。今天我们就来聊聊死锁以及怎么解决
锁类型mysql锁级别:页级、表级、行级

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低 。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高 。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
死锁产生原因和示例1、产生原因:死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程 。表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的InnoDB 。
死锁的关键在于:两个(或以上)的Session加锁的顺序不一致 。
那么对应的解决死锁问题的关键就是:让不同的session加锁有次序
2、产生示例:案例一需求:将投资的钱拆成几份随机分配给借款人 。
起初业务程序思路是这样的:
投资人投资后,将金额随机分为几份,然后随机从借款人表里面选几个,然后通过一条条select for update 去更新借款人表里面的余额等 。
例如两个用户同时投资,A用户金额随机分为2份,分给借款人1,2
B用户金额随机分为2份,分给借款人2,1
由于加锁的顺序不一样,死锁当然很快就出现了 。
对于这个问题的改进很简单,直接把所有分配到的借款人直接一次锁住就行了 。
Select * from xxx where id in (xx,xx,xx) for update在in里面的列表值mysql是会自动从小到大排序,加锁也是一条条从小到大加的锁
第一个会话:
注意:关闭掉自动提交set autocommit=0;
【MySQL死锁产生的原因和解决方法】mysql> select * from goods where goods_id in (2,3) for update;+----+--------+------+---------------------+| good_id | goods_name | price|+----+--------+------+---------------------+|2 | bbbb| 1.00 ||3 | vvv| 3.00 |+----+--------+------+---------------------+第二个会话:
select * from goods where goods_id in (3,4,5) for update;锁等待中……
案例二在开发中,经常会做这类的判断需求:根据字段值查询(有索引),如果不存在,则插入;否则更新 。
以id为主键为例,目前还没有id=22的行
注意:关闭掉自动提交set autocommit=0;
第一个会话:
select * from goods where goods_id=22 for update;第二个会话:
select * from goods where goods_id=23for update;再在第一个会话:
insert into goods values(22,'ac',11.5);锁等待中……
再到第二个会话:
insert into goods values(23,'bc',23.0);ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
当对存在的行进行锁的时候(主键),mysql就只有行锁 。
当对未存在的行进行锁的时候(即使条件为主键),mysql是会锁住一段范围
锁住的范围为:
(无穷小或小于表中锁住id的最大值,无穷大或大于表中锁住id的最小值)
如一:如果表中目前有已有的id为(11,12)
那么就锁住(12,无穷大)
如二:如果表中目前已有的id为(11,30)
那么就锁住(11,30)
对于这种死锁的解决办法是:
insert into goods(xx,xx) on duplicate key update `xx`='XX';用mysql特有的语法来解决此问题 。因为insert语句对于主键来说,插入的行不管有没有存在,都会只有行锁
死锁检查处理正常情况下,死锁发生时,权重最小的连接将被kill并回滚 。但是为了找出语句来优化,启用可启用死锁将死锁信息记录下来 。
#step 1:窗口一mysql> start transaction;mysql> update aa set name='aaa' where id = 1; #step 2:窗口二mysql> start transaction;mysql> update bb set name='bbb' where id = 1; #step 3:窗口一mysql> update bb set name='bbb';查看方式
#step 4:窗口三#是否自动提交mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit| ON|+---------------+-------+ 查看当前链接


推荐阅读