MySQL死锁产生原因和解决方法
阅读本文大概需要 8.5 分钟。
来自:r6d.cn/qDxd
Mysql 锁类型
一、锁类型介绍:
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
next KeyLocks锁,同时锁住记录(数据),并且锁住记录前面的Gap
Gap锁,不锁记录,仅仅记录前面的Gap
Recordlock锁(锁数据,不锁Gap)
所以其实 Next-KeyLocks=Gap锁+ Recordlock锁
二、死锁产生原因和示例
1、产生原因:
2、产生示例:
案例一
Select * from xxx where id in (xx,xx,xx) for update
mysql> select * from t3 where id in (8,9) for update;
+----+--------+------+---------------------+
| id | course | name | ctime |
+----+--------+------+---------------------+
| 8 | WA | f | 2016-03-02 11:36:30 |
| 9 | JX | f | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
rows in set (0.04 sec)
select * from t3 where id in (10,8,5) for update;
mysql> select * from t3 where id=5 for update;
mysql> select * from t3 where id=10 for update;
+----+--------+------+---------------------+
| id | course | name | ctime |
+----+--------+------+---------------------+
| 10 | JB | g | 2016-03-10 11:45:05 |
+----+--------+------+---------------------+
row in set (0.00 sec)
案例二
select * from t3 where id=22 for update;
Empty set (0.00 sec)
select * from t3 where id=23 for update;
Empty set (0.00 sec)
insert into t3 values(22,'ac','a',now());
insert into t3 values(23,'bc','b',now());
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
insert into t3(xx,xx) on duplicate key update `xx`='XX';
案例三
mysql> select * from t3 where id=9 for update;
+----+--------+------+---------------------+
| id | course | name | ctime |
+----+--------+------+---------------------+
| 9 | JX | f | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
row in set (0.00 sec)
mysql> select * from t3 where id<20 for update;
mysql> insert into t3 values(7,'ae','a',now());
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
案例四
案例五
案例六
CREATE TABLE dltask (
id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT ‘auto id’,
a varchar(30) NOT NULL COMMENT ‘uniq.a’,
b varchar(30) NOT NULL COMMENT ‘uniq.b’,
c varchar(30) NOT NULL COMMENT ‘uniq.c’,
x varchar(30) NOT NULL COMMENT ‘data’,
PRIMARY KEY (id),
UNIQUE KEY uniq_a_b_c (a, b, c)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’deadlock test’;
delete from dltask where a=? and b=? and c=?;
找到满足条件的记录,并且记录有效,则对记录加X锁,No Gap锁(lock_mode X locks rec but not gap);
找到满足条件的记录,但是记录无效(标识为删除的记录),则对记录加next key锁(同时锁住记录本身,以及记录之前的Gap:lock_mode X);
未找到满足条件的记录,则对第一个不满足条件的记录加Gap锁,保证没有满足条件的记录插入(locks gap before rec);
死锁预防策略
根据给定的查询条件,找到对应的记录所在页面;
对页面加上X锁(RWLock),然后在页面内寻找满足条件的记录;
在持有页面锁的情况下,对满足条件的记录加事务锁(行锁:根据记录是否满足查询条件,记录是否已经被删除,分别对应于上面提到的3种加锁策略之一);
剖析死锁的成因
delete from dltask where a=’a’ and b=’b’ and c=’c’;
Delete操作,针对的是唯一索引上的等值查询的删除;(范围下的删除,也会产生死锁,但是死锁的场景,跟本文分析的场景,有所不同)
至少有3个(或以上)的并发删除操作;
并发删除操作,有可能删除到同一条记录,并且保证删除的记录一定存在;
事务的隔离级别设置为Repeatable Read,同时未设置innodb_locks_unsafe_for_binlog参数(此参数默认为FALSE);(Read Committed隔离级别,由于不会加Gap锁,不会有next key,因此也不会产生死锁)
使用的是InnoDB存储引擎;(废话!MyISAM引擎根本就没有行锁)
如何检测代码运行是否出现死锁
https://blog.csdn.net/kk185800961/article/details/79528841
https://blog.csdn.net/yucaifu1989/article/details/79400446
微信扫描二维码,关注我的公众号
朕已阅