Classic and common deadlock causes and solutions when Mysql is concurrent

 1. What locks does mysql have?

 

MySQL has three lock levels: page level, table level, and row level.

Table-level lock: low overhead and fast locking; no deadlock; large locking granularity, the highest probability of lock conflict, and the lowest concurrency.

Row-level locks: high overhead, slow locking; deadlocks; the smallest locking granularity, the lowest probability of lock conflicts, and the highest concurrency.

Page locks: overhead and locking time are between table locks and row locks; deadlocks can occur; locking granularity is between table locks and row locks, and the degree of concurrency is average

 

algorithm:

next KeyLocks lock, lock the record (data) at the same time, and lock the Gap in front of the record    

Gap lock, do not lock the record, only record the previous gap

Recordlock lock (lock data, not lock Gap)

So actually Next-KeyLocks = Gap lock + Recordlock lock

 

2. Under what circumstances will it cause deadlock

 

The so-called deadlock <DeadLock>: refers to a phenomenon in which two or more processes are
waiting for each other due to competition for resources during the execution process. If there is no external force, they will not be able to advance.
At this time, it is called The system is in a deadlock state or the system has a deadlock. These processes that are always waiting for each other are called deadlock processes.
Table-level locks will not cause deadlocks. So the solution to deadlocks is mainly for the most commonly used InnoDB.

 

The key to deadlock is that the order of locking of two (or more) sessions is inconsistent.

Then the corresponding key to solving the deadlock problem is: let different sessions lock in order

 

3. Some common deadlock cases

 

Case number one:

Demand: split the invested money into several random allocations to borrowers.

The initial business process idea is as follows:

After the investor invests, the amount is randomly divided into several parts, and then a few are randomly selected from the borrower table, and then the balance in the borrower table is updated by selecting for update one by one.

 

The abstraction is that a session will have several statements as follows through the for loop:

Select * from xxx where id='随机id' for update

 

Basically, the program will deadlock shortly after it is started.

This is arguably the most classic deadlock situation.

 

例如两个用户同时投资,A用户金额随机分为2份,分给借款人1,2

B用户金额随机分为2份,分给借款人2,1

由于加锁的顺序不一样,死锁当然很快就出现了。

 

对于这个问题的改进很简单,直接把所有分配到的借款人直接一次锁住就行了。

Select * from xxx where id in (xx,xx,xx) for update

在in里面的列表值mysql是会自动从小到大排序,加锁也是一条条从小到大加的锁

 

 

复制代码
例如(以下会话id为主键):

Session1:

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 |

+----+--------+------+---------------------+

2 rows in set (0.04 sec)

 

 

Session2:

select * from t3 where id in (10,8,5) for update;

锁等待中……

其实这个时候id=10这条记录没有被锁住的,但id=5的记录已经被锁住了,锁的等待在id=8的这里。

 

不信请看

Session3:

mysql> select * from t3 where id=5 for update;

锁等待中

 

Session4:

mysql> select * from t3 where id=10 for update;

+----+--------+------+---------------------+

| id | course | name | ctime               |

+----+--------+------+---------------------+

| 10 | JB     | g    | 2016-03-10 11:45:05 |

+----+--------+------+---------------------+

1 row in set (0.00 sec)

 

在其它session中id=5是加不了锁的,但是id=10是可以加上锁的。
复制代码

 

 

 

 

案例2:

在开发中,经常会做这类的判断需求:根据字段值查询(有索引),如果不存在,则插入;否则更新。

 

复制代码
以id为主键为例,目前还没有id=22的行

Session1:

select * from t3 where id=22 for update;

Empty set (0.00 sec)

 

session2:

select * from t3 where id=23  for update;

Empty set (0.00 sec)

 

Session1:

insert into t3 values(22,'ac','a',now());

锁等待中……

 

Session2:

insert into t3 values(23,'bc','b',now());

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

 
复制代码

 

当对存在的行进行锁的时候(主键),mysql就只有行锁。

当对未存在的行进行锁的时候(即使条件为主键),mysql是会锁住一段范围(有gap锁)

 

 

锁住的范围为:

(无穷小或小于表中锁住id的最大值,无穷大或大于表中锁住id的最小值)

 

如:如果表中目前有已有的id为(11 , 12)

那么就锁住(12,无穷大)

如果表中目前已有的id为(11 , 30)

那么就锁住(11,30)

 

对于这种死锁的解决办法是:

insert into t3(xx,xx) on duplicate key update `xx`='XX';

 

用mysql特有的语法来解决此问题。因为insert语句对于主键来说,插入的行不管有没有存在,都会只有行锁。

 

 

案例3:

直接上情景:

复制代码
mysql> select * from t3 where id=9 for update;

+----+--------+------+---------------------+

| id | course | name | ctime               |

+----+--------+------+---------------------+

|  9 | JX     | f    | 2016-03-01 11:36:30 |

+----+--------+------+---------------------+

1 row in set (0.00 sec)

 

Session2:

mysql> select * from t3 where id<20 for update;

锁等待中

 

Session1:

mysql> insert into t3 values(7,'ae','a',now());

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

 
复制代码

 

 

这个跟案例一其它是差不多的情况,只是session1不按常理出牌了,

Session2在等待Session1的id=9的锁,session2又持了1到8的锁(注意9到19的范围并没有被session2锁住),最后,session1在插入新行时又得等待session2,故死锁发生了。

 

这种一般是在业务需求中基本不会出现,因为你锁住了id=9,却又想插入id=7的行,这就有点跳了,当然肯定也有解决的方法,那就是重理业务需求,避免这样的写法。

 

 

附记,推荐两篇好文章

案例4:

http://hedengcheng.com/?p=844

 

MySQL 加锁处理分析:

http://hedengcheng.com/?p=771



https://www.cnblogs.com/zejin2008/p/5262751.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325644994&siteId=291194637