Ali Ermian: How to solve the MySQL deadlock problem?

We use MySQL with a high probability of encountering deadlock problems, which is really a very troublesome problem. This article will introduce the deadlock, analyze and discuss common deadlock cases, and give some suggestions on how to avoid deadlock as much as possible.

1. What is deadlock

Deadlock is a common problem in concurrent systems, and it also occurs in the concurrent read and write request scenarios of database MySQL. A "deadlock" occurs when two or more transactions are waiting for each other to release the locks they already hold or wait for lock resources in a circular manner due to inconsistency in the order of locking. Common error messages are Deadlock found when trying to get lock....

For example, transaction A holds X1 lock and applies for X2 lock, and transaction B holds X2 lock and applies for X1 lock. Transactions A and B hold locks and apply for the locks held by each other to enter a circular wait, resulting in a deadlock.

picture

As shown in the figure above, the resource requests of the four cars on the right generate a loop phenomenon, that is, an infinite loop, which leads to a deadlock.

From the definition of deadlock, several elements of MySQL deadlock are:

  1. two or more transactions

  2. Each transaction already holds a lock and applies for a new lock

  3. Lock resources can only be held by the same transaction at the same time or are incompatible

  4. Transactions wait for each other circularly due to holding locks and applying for locks

2. InnoDB lock type

In order to analyze deadlocks, it is necessary to have an understanding of InnoDB's lock types.

picture

The MySQL InnoDB engine implements the standard行级别锁:共享锁( S lock ) 和排他锁 ( X lock )

  1. Different transactions can simultaneously add S locks to the same row of records.

  2. If a transaction adds X lock to a row record, other transactions cannot add S lock or X lock, resulting in lock waiting.

If transaction T1 holds the S lock on row r, then when another transaction T2 requests the lock on r, it will do the following:

  1. T2 requests the S lock to be granted immediately, and as a result T1 and T2 both hold the S lock for row r

  2. T2 request X lock cannot be allowed immediately

If T1 holds the X lock of r, then T2's request for the X and S locks of r cannot be allowed immediately, and T2 must wait for T1 to release the X lock, because the X lock is not compatible with any locks. The compatibility of shared locks and exclusive locks is as follows:

picture

2.1, gap lock (gap lock) 

A gap lock locks a gap to prevent insertion. Assuming that the index column has three values ​​of 2, 4, and 8, if 4 is locked, the two gaps (2,4) and (4,8) will also be locked at the same time. Other transactions cannot insert records with index values ​​between these two gaps. However, there is an exception to gap locks:

  1. If the index column is a unique index, then only this record is locked (only row locks are added), and the gap is not locked.

  2. For a joint index and a unique index, if the where condition only includes a part of the joint index, a gap lock will still be added.

2.2、next-key lock

Next-key lock is actually a combination of row lock + gap lock in front of this record. Assuming index values ​​10, 11, 13 and 20, then possible next-key locks include:

(negative infinity,10],(10,11],(11,13],(13,20],(20,positive infinity)

Under the RR isolation level, InnoDB uses next-key lock mainly to prevent 幻读problems.

2.3. Intention lock

InnoDB allows row locks and table locks to exist at the same time in order to support multi-granularity locking. In order to support locking operations at different granularities, InnoDB supports an additional locking method called Intention Lock. Intention locks divide locked objects into multiple levels. Intention locks mean that transactions want to lock at a finer granularity. There are two types of intent locks:

  1. Intent Shared Lock (IS): The transaction intentionally adds a shared lock to some rows in the table

  2. Intentional exclusive lock (IX): The transaction intentionally adds an exclusive lock on some rows in the table

Since the InnoDB storage engine supports row-level locks, intent locks do not actually block any requests other than full table scans. The compatibility of table-level intent locks with row-level locks is as follows:

picture

2.4. Insert Intention lock 

Insert intent lock is a kind of gap lock set before inserting a row record operation. This lock releases a signal of insert mode, that is, when multiple transactions are inserted in the same index gap, if they are not inserted in the same position in the gap, they do not need to be inserted. wait for each other. Assuming that a column has index values ​​of 2 and 6, as long as two transactions are inserted in different positions (eg, transaction A inserts 3, and transaction B inserts 4), they can be inserted at the same time.

2.5. Lock Mode Compatibility Matrix

Horizontal is the lock held, vertical is the lock being requested:

picture

3. Read the deadlock log 

Before conducting a specific case analysis, let's first understand how to read the deadlock log, and use the information in the deadlock log as much as possible to help us solve the deadlock problem.

The database scenario of the following test case is as follows:MySQL 5.7 事务隔离级别为 RR

The table structure and data are as follows:

picture

The test case is as follows:

picture

 You can view the latest deadlock log by executing show engine innodb status.

3.1. The log analysis is as follows:

1.***** (1) TRANSACTION: TRANSACTION 2322, ACTIVE 6 sec starting index read

 The transaction number is 2322, active for 6 seconds, and starting index read indicates that the transaction status is to read data according to the index. Common other states are:

picture

mysql tables in use 1 Indicates that the current transaction uses a table.

locked 1 Indicates that there is a table lock on the table, which is LOCK_IX for DML statements

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)

LOCK WAIT Indicates that it is waiting for a lock, 2 lock struct(s) indicating that the length of the trx->trx_locks lock list is 2, and each chain node represents a lock structure held by the transaction, including table locks, record locks, and self-increment locks. In this use case 2locks means IX lock and lock_mode X (Next-key lock)

1 row lock(s) Indicates the number of row record locks/gap locks held by the current transaction.

MySQL thread id 37, OS thread handle 140445500716800, query id 1234 127.0.0.1 root updating

MySQL thread id 37 Indicates that the thread ID that executes the transaction is 37 (ie show processlist; the displayed ID)

delete from student where stuno=5 Indicates the sql that transaction 1 is executing. The more uncomfortable thing is  show engine innodb status that the complete sql cannot be viewed. Usually, the sql currently waiting for the lock is displayed.

 ***** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2322 lock_mode X waiting

RECORD LOCKS means record lock. This entry means that transaction 1 is waiting for the X lock of idx_stuno on the table student, which is actually Next-Key Lock in this case.

The log of transaction 2 is similar to the above analysis:

2.***** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X

Show that insert into student(stuno,score) values(2,10) of transaction 2 holds Lock mode X with a=5

 LOCK_gap, but we can't see the delete from student where stuno=5; executed by transaction 2 from the log

This is also the root cause of the problem that it is difficult for DBAs to analyze deadlocks only based on logs.

3.***** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X locks gap before rec insert intention waiting

Indicates that the insert statement of transaction 2 is waiting for the insert intention lock lock_mode X locks gap before rec insert intention waiting ( LOCK_X + LOCK_REC_gap )

4. Classic case analysis

4.1, transaction concurrent insert unique key conflict

The table structure and data are as follows:

picture

picture

The test case is as follows:

picture

 The log analysis is as follows:

  1. Transaction T2 insert into t7(id,a) values ​​(26,10) The insert statement is successful, holding a=10 排他行锁( Xlocks rec but no gap )

  2. Transaction T1 insert into t7(id,a) values ​​(30,10), because the first insert of T2 has inserted the record of a=10, transaction T1 insert a=10 will cause a unique key conflict, and it is necessary to apply for a unique key to the conflict Index plus S Next-key Lock (ie lock mode S waiting) This is a 间隙锁gap area between (,10], (10,20] that will apply for locking.

  3. Transaction T2 insert into t7(id, a) values ​​(40, 9) The value of a=9 inserted by this statement is applied for in transaction T1  gap 锁4-10之间, so the second insert statement of transaction T2 needs to wait for the  S-Next-key Lock 锁release of transaction T1, and in the log shows lock_mode X locks gap before rec insert intention waiting .

 4.2. Concurrent deadlock problem of update first and then insert

 The table structure is as follows, no data:

picture

The test case is as follows:

picture

 Deadlock analysis:
It can be seen that the records that do not exist in the update of the two transactions are obtained successively . The 间隙锁( gap 锁)gap locks are compatible, so they will not block in the update link. Both hold the gap lock and then compete for insertion 意向锁. When there are other sessions holding gap locks, the current session cannot apply for the insertion intent lock, resulting in deadlock.

5. How to avoid deadlock as much as possible

  1. Reasonably design the index, and put the column with high degree of distinction in front of the composite index, so that the business SQL can pass the index as much as possible 定位更少的行,减少锁竞争.

  2. Adjust the execution order of business logic SQL to avoid update/delete SQL that holds locks for a long time before the transaction.

  3. Avoid 大事务, try to split large transactions into multiple small transactions for processing, and the probability of lock conflicts in small transactions is also smaller.

  4. to 固定的顺序access tables and rows. For example, for two transactions that update data, the order of updating data in transaction A is 1, 2; the order of updating data in transaction B is 2, 1. This is more likely to cause deadlocks.

  5. In systems with high concurrency, do not explicitly lock, especially in transactions. Such as select ... for update statement, if it is in a transaction (运行了 start transaction 或设置了autocommit 等于0), it will lock the found records.

  6. Try 主键/索引to find records by search, range search increases the possibility of lock conflicts, and do not use the database to do some extra quota calculation work. For example, some programs will use statements such as "select ... where ... order by rand();". Since such statements do not use indexes, the data of the entire table will be locked.

  7. Optimize SQL and table design to reduce the use of too many resources at the same time. For example, 减少连接的表to break complex SQL  分解into multiple simple SQLs.

Guess you like

Origin blog.csdn.net/qq_34272760/article/details/122241551