当前位置:首页 » Mysql技术教程

项目中遇到的MySql行锁与并发性关系(3)--发生deadlock的原因

2014-05-16 00:11 本站整理 浏览(7036)

项目中遇到的MySql行锁与并发性关系(3)--发生deadlock的原因,有需要的朋友可以参考下。

重点到了!发生deadlock的原因:

deadlock是死锁,一般发生的原因就是两个同时加上事务的连接互相UPDATE对方的加锁行,导致互相等待,这样的操作逻辑上会永远执行下去,这应该就是死锁的概念,但是数据库不会让这种情况真正发生,当判断出发生这种情况的时候MySql会先中断并且回滚后执行的连接并且报deadlock异常,同时允许先进行DML操作的连接,示例如下:

然后在连接1中加事务和对表TEST_ALR的ID=1行加行锁

SET autocommit=0;
SELECT * FROM TEST_ALR WHERE ID=1 FOR UPDATE;

然后在连接2中加事务和对表TEST_ALR的ID=2行加行锁,同时把ID=4的行进行UPDATE操作

SET autocommit=0;
SELECT * FROM TEST_ALR WHERE ID=2 FOR UPDATE;
UPDATE TEST_ALR SET NAME='ABC' WHERE ID=4;

此时都执行成功了,这两个连接之间也没有影响

此时在连接1中对ID=2行进行UPDATE,出现等待,在没报等待超时之前在连接2中对ID=1行进行UPDATE:

UPDATE TEST_ALR SET NAME='TEST2' WHERE ID=2;
Deadlock found when trying to get lock; try restarting transaction

出现deadlock异常,说明发生了之前描述的情况

查看TEST_ALR表ID=4的数据:

SELECT * FROM TEST_ALR WHERE ID=4;
ID NAME
4 赵六

发现已经回滚

再切回连接1查看,发现此时连接1的UPDATE赢成功执行完成:

1 row(s) affected

之前的项目因为需要用到并发,所以需要并发的CALL存储过程,当时的过程写的存在一些问题,行锁是上对了,但是加完行锁之后,后面的UPDATE操作不是以索引列为条件,所以造成了虽然连接中最开始写的行锁是加的行锁,但是到UPDATE操作的时候还是想对当前表进行表级锁操作,但是另外一个连接中也同时对此表加行级锁了,所以要等待行级锁事务解除,这边才能继续执行UPDATE操作,但是另外一边因为是CALL的同一个过程,所以也是会出现一个一模一样的UPDATE操作,所以这两个连接之间出现了互相等待的情况造成了死锁,示例如下:

现在连接1和连接2都加上事务和不同行的行锁:

连接1:

SET autocommit=0;
SELECT * FROM TEST_ALR WHERE ID=1 FOR UPDATE;

连接2:

SET autocommit=0;
SELECT * FROM TEST_ALR WHERE ID=2 FOR UPDATE;
此时在连接1中以NAME当做条件对TEST_ALARM的进行UPDATE(张三在表中其实是唯一的,但是不是索引列):

UPDATE TEST_ALR SET NAME ='张三1' WHERE NAME='张三';

发现出现等待,在未超时的时候在连接2中执行同样原理的UPDATE:

UPDATE TEST_ALR SET NAME ='TEST' WHERE NAME='TEST';
出现死锁异常:

EooroCode:1213

Deadlock found when trying to get lock; try restarting transaction

在切回连接1中:

1 row(s) affected
其实在TEST_ALR表中的NAME字段的'张三'和'TEST'是分别对应着自己所在行的数据,但是当你的UPDATE操作条件不是根据索引列操作的话,数据库不会去实际读取表中的数据,所以数据库并不知道你的条件是否在之前加的行锁的范围内,或者说因为UPDATE操作的条件不是索引列,所以会重新对表加上表级锁,导致的互相等待造成的deadlock;