死锁浅析

1.锁的分类

死锁浅析_第1张图片

首先记住一个基本点,锁是对表的索引的节点进行锁,而不是对某行锁,当走不了索引时候可能就会锁表了。

1.1锁模式

这里我主要分析锁的模式。

记录锁

记录锁是最简单的行锁, 仅仅锁住一行 。即使一个表没有索引,InnoDB也会隐式的创建一个索引,并使用这个索引实施记录锁。(record lock)

间隙锁(Gap Locks)

间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。主要对一个区间而加锁。关键字(gap before rec)

Next-Key Locks

Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。

插入意向锁(Insert Intention)

插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插入方式的信号。

锁模式兼容矩阵(横向是已持有锁,纵向是正在请求的锁):

死锁浅析_第2张图片

2.实现死锁

实现死锁的步骤详细看下面的链接。

这里实现死锁之前的sql。

死锁浅析_第3张图片

3.如读死锁日志

使用 show engine innodb status 查看死锁日志。

如何实现死锁,详细可以看链接文章,由于时间关系借了其的死锁日志简单分析。

“//”则是自己转述的话


2020-04-11 00:35:55 0x243c
*** (1) TRANSACTION: //表示第一个事务
TRANSACTION 38048, ACTIVE 92 sec inserting(2020-04-11 00:35:55 0x243c
*** (1) TRANSACTION:
TRANSACTION 38048, ACTIVE 92 sec inserting //这个事务执行插入sql
mysql tables in use 1, locked 1 //这个表存在锁
LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2  //正在等待锁
MySQL thread id 53, OS thread handle 2300, query id 2362 localhost ::1 root update
insert into account values(null,'Jay',100)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: //正在等待锁被释放
RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account` //希望锁的索引
trx id 38048 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 //这个比较重要大概意思是 准备插入但是正在等待获取获取意向插入锁
//下面则是表示 这个锁会锁的范围 
//重点留有这个范围 标记为 A
 0: len 3; hex 576569; asc Wei;;(不包含Wei)
 1: len 4; hex 80000002; asc     ;;(未知)
 
*** (2) TRANSACTION: //表示第二个事务
TRANSACTION 38049, ACTIVE 72 sec inserting, thread declared inside InnoDB 5000 //这个事务执行插入sql
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 52, OS thread handle 9276, query id 2363 localhost ::1 root update
insert into account  values(null,'Yan',100)
*** (2) HOLDS THE LOCK(S): //重要点 这个事务锁上了一个锁
RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account` //这个锁 锁的索引
trx id 38049 lock_mode X locks gap before rec //表示上了一个排他间隙锁
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
//这个锁 锁的索引的详细的节点范围
//重点留有这个范围 标记为 B ,可见和A的存在交集,则会冲突
 0: len 3; hex 576569; asc Wei;;(不包含Wei)
 1: len 4; hex 80000002; asc     ;;(未知)
 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED: //同是第二个事务 等待锁
RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account`  //希望锁的索引
trx id 38049 lock_mode X insert intention waiting //等待插入意向锁
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
//等待的锁的索引范围 可见和A的存在交集,则会冲突
 0: len 8; hex 73757072656d756d; asc supremum;; (无限)

上面的日志可以简单分析:
死锁浅析_第4张图片

事务B执行更新时候 会锁了部分节点的索引,然后 事务A 执行插入 数据的索引节点刚刚好和事务B的持有的索引锁冲突了,A则等待B释放,然后B又则行了insert的索引节点需要的锁和A希望持有的冲突的了。锁会排队,先申请先获取的,所以就会 A等B B等A的情况出现死锁。

死锁浅析_第5张图片

我认为 可以先判断锁之间的互斥关系,然后互斥,则判断其锁的范围,如果还是冲突,则会死锁了。

4.死锁解决方法

出现死锁,一般都会设置超时的时间的,也可以通过命令是用来查看当前运行的所有事务:

SELECT * FROM information_schema.INNODB_TRX;

死锁浅析_第6张图片

然后 killtrx_mysql_thread_id

再排除出现死锁的操作,从而优化代码架构。

5.避免方法

简单的建议:

  1. 高并发前提对操作相关数据源加上锁,常见是分布式锁,for update。

  2. 尽量减少大事务。

  3. 单个事务执行时间尽量缩短。

感谢参考链接:

https://www.tuicool.com/articles/zEvU3me

你可能感兴趣的:(mysql,mysql)