MySQL deadlocked, what should I do?

Hello everyone, my name is Xiaolin.

Let me say that I have encountered database deadlock problems a long time ago.

The main business logic is to add orders, modify orders, query orders and other operations. Then, because the order cannot be repeated, an idempotency check was done when adding an order. The method is to query whether the order exists through the select ... for updatestatement insert the order record if it does not exist.

It is precisely because of this operation that deadlocks may occur when the business volume is large.

Next, I will talk to you about why deadlocks occur and how to avoid them .

occurrence of deadlock

In this case, the storage engine Innodb is used, and the isolation level is non-repeatable read (RR).

Next, I will show you how the deadlock happens in a practical way.

I built an order table, in which the id field is the primary key index, and the order_no field is a common index, that is, a non-unique index:

CREATE TABLE `t_order` (
  `id` int NOT NULL AUTO_INCREMENT,
  `order_no` int DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_order` (`order_no`) USING BTREE
) ENGINE=InnoDB ;

Then, the first t_ordertable now has 6 records:

picture

Suppose there are two transactions at this time, one transaction needs to insert order 1007, and the other transaction needs to insert order 1008. Because the order needs to be checked for idempotency, the two transactions must first check whether the order exists, and then insert the record if it does not exist. , the process is as follows:

picture

It can be seen that both transactions are in a waiting state (provided that deadlock detection is not turned on), that is, a deadlock occurs because they are both waiting for each other to release the lock.

Here, when querying whether a record exists, the select ... for updatestatement to prevent the problem of phantom reading caused by other transactions inserting records during transaction execution.

If the select ... for updatestatement , but a simple select statement is used, if two requests with the same order number come in at the same time, two duplicate orders will appear, and phantom reading may occur, as shown in the following figure:

picture

Why do deadlocks occur?

At the repeatable read isolation level, there is a phantom read problem.

To solve the phantom read problem under the "repeatable read" isolation level, the Innodb engine introduces the next-key lock , which is a combination of record lock and gap lock.

  • Record Loc, record lock, the lock is the record itself;
  • Gap Lock, a gap lock, locks the gap between two values ​​to prevent other transactions from inserting new data in this gap, thereby avoiding the phenomenon of phantom reading.

The ordinary select statement will not lock the record, because it is a snapshot read through the MVCC mechanism. If you want to add a row lock to the record during query, you can use the following two methods:

begin;
//对读取的记录加共享锁
select ... lock in share mode;
commit; //锁释放

begin;
//对读取的记录加排他锁
select ... for update;
commit; //锁释放

The release timing of the row lock is that after the transaction is committed, the lock will be released, not the row lock will be released after a statement is executed.

For example, the query statement of transaction A below will lock (2, +∞]the records of the range, and then if any other transaction inserts data in the locked range during the period, it will be blocked.

picture

The locking rules of next-key locks are actually quite complicated. In some scenarios, they will degenerate into record locks or gap locks. I also wrote a locking rule before. For details, you can read this article " I did a day's experiment! "

It should be noted that the next-key lock locks the index, not the data itself, so if the where condition of the update statement does not use the index column, then a full table scan will be performed. The row lock is added, and the gap lock is also added to the gap on both sides of the row, which is equivalent to locking the entire table, and then the lock will not be released until the end of the transaction.

So don't execute the update statement without index conditions online, otherwise it will cause business stagnation. I have a reader who did this and was educated by the boss. For details, please read this article " It's over , the company was An update statement is dead! "

Going back to the previous deadlock example, when executing the following statement:

select id from t_order where order_no = 1008 for update;

Because order_no is not a unique index, the type of row lock is gap lock, so the range of gap lock is (1006, +∞). Then, when transaction B inserts the record with id = 1008 into the gap lock, it will be locked.

Because when we execute the following insert statement, the insert intent lock is acquired again on the insert gap.

insert into t_order (order_no, create_date) values (1008, now());

Insertion intent locks conflict with gap locks, so when other transactions hold a gap lock for the gap, they need to wait for other transactions to release the gap lock before acquiring the insert intent lock. The gap lock is compatible with the gap lock, so the select ... for updatestatements will not affect each other.

In the case, transaction A and transaction select ... for updateB both hold (1006,+∞)a gap lock with a range of 1 after the statement is executed, and the next insert operation is waiting for the gap lock of the other transaction to be released in order to obtain the insertion intention lock, which results in Circular wait, leading to deadlock.

How to avoid deadlock?

There are four necessary conditions for deadlock: mutual exclusion, possession and waiting, non-forcible occupation, and circular waiting . As long as the system deadlocks, these conditions must be established, but as long as any one of the conditions is violated, the deadlock will not be established.

At the database level, there are two strategies to release the deadlock state by "breaking the circular wait condition":

  • Sets the timeout for transactions waiting for locks . When the waiting time of a transaction exceeds this value, the transaction is rolled back, the lock is released, and another transaction can continue to execute. In InnoDB, the parameter innodb_lock_wait_timeoutis used to set the timeout, the default value is 50 seconds.

    When a timeout occurs, the following prompt appears:

picture

  • Enable active deadlock detection . Active deadlock detection After a deadlock is found, a transaction in the deadlock chain is actively rolled back, so that other transactions can continue to execute. innodb_deadlock_detectSetting the parameter to on means to enable this logic, which is enabled by default.

    When a deadlock is detected, the following prompt will appear:

picture

The above two strategies are the way to avoid "when deadlock occurs".

We can go back to the business point of view to prevent deadlocks. The purpose of idempotency checking on orders is to ensure that there will be no duplicate orders. Then we can directly set the order_no field as a unique index column, and use its uniqueness to guarantee The order table does not have duplicate orders, but the downside is that an exception will be thrown when we insert an existing order record.


Finally a paragraph:

Interviewer: Explain what is deadlock?

Candidate: You hire me, I'll tell you

Interviewer: You tell me and I will hire you

Candidate: You hire me, I'll tell you

Interviewer: Fuck off!

That's it for today's sharing.

Guess you like

Origin blog.csdn.net/qq_34827674/article/details/123447989