目录
一、MySQL出现死锁的场景
二、MySQL当中的死锁现象
三、Insert语句怎样加锁的
隐式锁&显示锁
记录之间加有间隙锁
遇到唯一键冲突或者主键冲突的时候加锁
如果主键索引重复:
如果唯一二级索引重复:
四、如何避免MySQL当中的死锁现象
方案一、设置任务超时等待时间
方案二、主动开启死锁检测
方案三、对于更新频繁的字段,采用唯一索引的设置方案
本文为博主参考网站《小林coding》的学习笔记,具体内容请参考转载网站
关于什么是死锁问题,已经在这一篇文章当中提到了。这是Java当中的死锁问题:Java对于synchronized的初步认识_革凡成圣211的博客-CSDN博客synchronized,死锁,https://blog.csdn.net/weixin_56738054/article/details/128062475?spm=1001.2014.3001.5501
死锁的准确定义:就是在一组线程当中,他们竞争同一资源而造成相互阻塞的现象。
那么,在mysql当中,也会出现死锁,这里的死锁又是什么呢?
假设有下面这一张表:
id(主键索引) | no(非主键索引) | name |
1 | 1001 | 小明 |
2 | 1002 | 小李 |
3 | 1003 | 小华 |
4 | 1004 | 小黄 |
在这一张表当中,id为主键索引,为二级索引,name这一列没有任何索引的约束。
现在这张表当中,有以上的一些数据。
现在,有两个事物,一个事物A,另外一个事物B
下面,根据步骤,分析一下下面两个事物的执行流程:
第一步:
在上述两个事物当中,事物A首先开启了,然后执行一条查询的sql语句:也就是select...for update这样的语句。 因为记录的最大值为1004,1007不在这一个范围当中。此时,事物A对于表当中no范围为(1004,+∞)的no索引加上了一把锁间隙锁
第二步:
事物B开启了,因为no值为1008的记录,不在范围(1004,+∞)的范围之内。因此,事物B也会加一个间隙锁,范围是(1004,+∞);由于间隙锁之间是互容的,因此事物B在执行select语句的时候,不会发生阻塞。
第三步:
事物A执行了一条插入的索引为1007的数值。但是,由于事物B对于事物A插入的范围加上了间隙锁,因此事物A一定要等待到事物B释放锁,才可以继续执行
第四步:
事物B执行了一条插入的索引值为1008的sql语句。但是,由于事物A对于(1004,+∞)的范围加锁了。因此,事物B一定需要等待到事物A释放锁,才可以继续执行。
可以看到,此时,两个事物互相阻塞了。
Insert语句在正常执行的时候,是不会生成锁结构的,它是靠聚簇索引自带的一个被称为trx_id的字段来作为隐式锁来保护记录的。
在Insert的过程当中不加锁,只有在特殊的情况下面,才会把隐式锁转化为显示锁,也就是真正加锁的过程。
举两个例子来说明隐式锁转换为显示锁的场景:
①范围(a,b)内加有间隙锁,当有一条记录在范围(a,b)之内插入记录的时候,就会转化为显示锁。
②如果insert语句插入的记录和已有的记录之间出现了主键,也无法插入。
还是上面这个表:
id(主键索引) | no(非主键索引) | name |
1 | 1001 | 小明 |
2 | 1002 | 小李 |
3 | 1003 | 小华 |
4 | 1004 | 小黄 |
此时,这一张表当中,假如有一条语句,执行:
select * from t_order where order_no = 1006 for update;
此时,事物如果插入一条语句,insert....values(1007...),这个时候,由于插入的数据正好在前一个sql语句插入的范围之内,因此会被阻塞。
当隔离级别为读已提交的时候,对这一条记录的主键索引加S型记录锁;
当隔离级别为可重复读的时候,插入新记录的事物会给已存在的
不论是哪个隔离级别,插入新记录的事务都会给已存在的二级索引列值重复的二级索引记录添加 S 型 next-key 锁。
当在一个任务的等待时间超过了这个时间之后,就进行回滚;
在 InnoDB 中,参数 innodb_lock_wait_timeout
是用来设置超时时间的,默认值时 50 秒。
将参数 innodb_deadlock_detect
设置为 on。
当innodb检测发现死锁之后,就会进行回滚死锁的事物。
例如在上面的例子当中,可以把no字段设置成唯一索引。