Mysql的死锁

项目经常会出现mysql的死锁问题,当年年少总是想通过SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 查看被锁的事务,然后kill掉他,或者重启mysql,唉,治标不治本啊,下次还会出现这些问题,其实造成死锁大多数情况就是我们的sql写的不大好。

我们先来模拟一组死锁,然后通过查看死锁日志再反向推出死锁的原因。

首先我们新建一个表:

CREATE TABLE `t1` (

`a` int(11) NOT NULL DEFAULT '0',

`b` varchar(20) DEFAULT NULL,

`c` varchar(100) DEFAULT NULL,

PRIMARY KEY (`a`),

KEY `a_mes` (`b`) USING HASH

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

这里引擎用InnoDB,并且在b字段建了一个索引‘a_mes’,先在表里加几行数据:

INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('1', 'aaa', NULL);

INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('2', 'asd', NULL);

INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('3', 'axd', NULL);

INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('4', 'bbb', NULL);

然后我这里的事务级别是RR,可以查看下,select @@tx_isolation;

这里有个姿势点,事务级别是RR的时候,当进行IUD操作的时候,如果指定的是非唯一索引的那个字段,RR级别下,会加GAP锁,比如我这里b字段上有非唯一索引,当我执行语句DELETE FROM t1 WHERE b='aaa';的时候就会加GAP锁,也就是相当于在0-aaa,aaa-asd的范围加上了GAP锁,同时aaa也会加X锁。所以接下来模拟两个事务:

事务一:set autocommit=0;

1.DELETE FROM t1 WHERE b='aaa';

2.INSERT INTO t1 VALUES(19,'baa','1');

事务二:set autocommit=0;

1.DELETE FROM t1 WHERE b='bbb';

2.INSERT INTO t1 VALUES(16,'aab','1');

当事务一执行第1条语句时,正如前面所说aaa索引加了X锁,并且在aaa范围内加了GAP锁,这时事务二执行弟1条语句,同理bbb索引也会加了X锁,并且在axd-bbb,bbb-无穷大范围加了GAP锁,然后事务一继续执行第二条语句,这个时候由于事务二加了锁,事务一就等待锁,事务二执行弟2条语句后,就报错了,这个就模拟了一组死锁。

然后我们查看死锁日志:show engine innodb status \G

通过日志我们看到事务一在执行INSERT INTO t1 VALUES(19,'baa','1');这句的时候,他要去获得索引‘a_mes’上的X锁,但是获得不到,所以在waiting。。。而事务二,在索引a_mes上持有X锁,并且事务二在执行INSERT INTO t1 VALUES(16,'aab','1');这句的时候也是获得不到锁,然后处于waiting状态。。。

会查看死锁日志,懂得mysql的加锁机制,去反推业务的逻辑,去查找真正造成死锁的原因,然后解决他,如果不了解GAP锁推荐这篇文章,写的很好http://hedengcheng.com/?p=771#_Toc374698322

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容