How to deal with MySQL deadlock

background

一个死锁在MySQL发生在两个或多个事务相互持有和锁请求,创建依赖的循环。在交易系统中,死锁是生活中不可或缺的事实,并非完全可以避免的。

Write in front
来自percona官方文档https://www.percona.com/blog/2014/10/28/how-to-deal-with-mysql-deadlocks/
link

Mysql5.6

只能使用SHOW ENGINE INNODB STATUS命令查看最新的死锁。
但是,使用Percona Toolkit的pt-deadlock-logger,
您可以按给定的时间间隔从SHOW ENGINE INNODB STATUS中检索死锁信息,
并将其保存到文件或表中以进行后期诊断。

With MySQL 5.6, you can enable a new variable innodb_print_all_deadlocks so that all deadlocks in InnoDB are recorded in the mysqld error log.
(5.7.26 default OFF)

在进行所有诊断之前,最重要的做法是让应用程序捕获死锁错误(MySQL错误编号1213)并通过重试事务进行处理。

How to diagnose MySQL deadlock

Limitations of show engine innodb status

Only show the last statement executed in two transactions

MySQL死锁可能涉及两个以上的事务,但是“最新检测到的DEADLOCK”部分仅显示最后两个事务。此外,它仅显示在两个事务中执行的最后一条语句,并锁定创建周期的两个事务的锁。缺少的是可能已经真正获得了锁的早期语句。我将展示一些有关如何收集遗漏语句的提示。

example

1 141013 6:06:22
2 *** (1) TRANSACTION:
3 TRANSACTION 876726B90, ACTIVE 7 sec setting auto-inc lock
4 mysql tables in use 1, locked 1
5 LOCK WAIT 9 lock struct(s), heap size 1248, 4 row lock(s), undo log entries 4
6 MySQL thread id 155118366, OS thread handle 0x7f59e638a700, query id 87987781416 localhost msandbox update
7 INSERT INTO t1 (col1, col2, col3, col4) values (10, 20, 30, 'hello')
8 *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
9 TABLE LOCK table `mydb`.`t1` trx id 876726B90 lock mode AUTO-INC waiting
10 *** (2) TRANSACTION:
11 TRANSACTION 876725B2D, ACTIVE 9 sec inserting
12 mysql tables in use 1, locked 1
13 876 lock struct(s), heap size 80312, 1022 row lock(s), undo log entries 1002
14 MySQL thread id 155097580, OS thread handle 0x7f585be79700, query id 87987761732 localhost msandbox update
15 INSERT INTO t1 (col1, col2, col3, col4) values (7, 86, 62, "a lot of things"), (7, 76, 62, "many more")
16 *** (2) HOLDS THE LOCK(S):
17 TABLE LOCK table `mydb`.`t1` trx id 876725B2D lock mode AUTO-INC
18 *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
19 RECORD LOCKS space id 44917 page no 529635 n bits 112 index `PRIMARY` of table `mydb`.`t2` trx id 876725B2D lock mode S locks rec but not gap waiting
20 *** WE ROLL BACK TRANSACTION (1)

Parsing

Line 1 gives the time when the deadlock occurred. If your application code captures and logs a deadlock error that should have occurred, you can match this timestamp with the deadlock error timestamp in the application log. You will have the rolled back transaction. From there, retrieve all the statements in the transaction.

Lines 3 and 11 record the transaction number and activation time. If you regularly log the output of SHOW ENGINE INNODB STATUS (which is a good practice), you can search for the previous output using the transaction number, hoping to see more statements from the same transaction. ACTIVE seconds can indicate whether the transaction is a single statement or multiple statements.

On lines 4 and 12, the table being used and locked is only for the current statement. Therefore, the use of one table does not necessarily mean that the transaction involves only one table.

Lines 5 and 13, this is worth mentioning because it tells you how many changes the transaction has made, that is, "undo log entries", and how many row locks it holds, that is, "row locks." This information implies the complexity of the transaction.

Lines 6 and 14 record the thread ID, connecting host and connecting user. If you use different MySQL users for different application functions, which is another good practice, you can determine which application area the transaction comes from based on the connected host and user.

On line 9, for the first transaction, it only shows the lock it is waiting for, in this case, the AUTO-INC lock on table t1. Other possible values ​​are S (for shared locks) and X (for exclusive locks).

Lines 16 and 17 show the lock it holds for the second transaction. In this case, it is the AUTO-INC lock waiting for TRANSACTION (1).

Lines 18 and 19 show which lock TRANSACTION (2) is waiting for. In this case, it is a share on the primary key of another table rather than a gap record lock. There are few sources of shared record locks in InnoDB:
1) Use SELECT...LOCK IN SHARE MODE
2) Use foreign key reference records
3) INSERT INTO...SELECT, the shared lock on the source table The
current trx(2) statement is for table t1 The simple insertion, thus eliminating 1 and 3. By checking SHOW CREATE TABLE t1, you can confirm that the S lock is due to the foreign key constraint on the parent table t2.

Example 2: In the MySQL community version, each record lock has printed record content:


1 2014-10-11 10:41:12 7f6f912d7700
2 *** (1) TRANSACTION:
3 TRANSACTION 2164000, ACTIVE 27 sec starting index read
4 mysql tables in use 1, locked 1
5 LOCK WAIT 3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
6 MySQL thread id 9, OS thread handle 0x7f6f91296700, query id 87 localhost ro ot updating
7 update t1 set name = 'b' where id = 3
8 *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
9 RECORD LOCKS space id 1704 page no 3 n bits 72 index `PRIMARY` of table `tes t`.`t1` trx id 2164000 lock_mode X locks rec but not gap waiting
10 Record lock, heap no 4 PHYSICAL RECORD: n_fields 5; compact format; info bit s 0
11 0: len 4; hex 80000003; asc ;;
12 1: len 6; hex 000000210521; asc ! !;;
13 2: len 7; hex 180000122117cb; asc ! ;;
14 3: len 4; hex 80000008; asc ;;
15 4: len 1; hex 63; asc c;;
16
17 *** (2) TRANSACTION:
18 TRANSACTION 2164001, ACTIVE 18 sec starting index read
19 mysql tables in use 1, locked 1
20 3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
21 MySQL thread id 10, OS thread handle 0x7f6f912d7700, query id 88 localhost r oot updating
22 update t1 set name = 'c' where id = 2
23 *** (2) HOLDS THE LOCK(S):
24 RECORD LOCKS space id 1704 page no 3 n bits 72 index `PRIMARY` of table `tes t`.`t1` trx id 2164001 lock_mode X locks rec but not gap
25 Record lock, heap no 4 PHYSICAL RECORD: n_fields 5; compact format; info bit s 0
26 0: len 4; hex 80000003; asc ;;
27 1: len 6; hex 000000210521; asc ! !;;
28 2: len 7; hex 180000122117cb; asc ! ;;
29 3: len 4; hex 80000008; asc ;;
30 4: len 1; hex 63; asc c;;
31
32 *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
33 RECORD LOCKS space id 1704 page no 3 n bits 72 index `PRIMARY` of table `tes t`.`t1` trx id 2164001 lock_mode X locks rec but not gap waiting
34 Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bit s 0
35 0: len 4; hex 80000002; asc ;;
36 1: len 6; hex 000000210520; asc ! ;;
37 2: len 7; hex 17000001c510f5; asc ;;
38 3: len 4; hex 80000009; asc ;;
39 4: len 1; hex 62; asc b;;

Lines 9 and 10: "Space ID" is the table space ID, and "page number" gives the page where the record lock is located in the table space. "N bits" is not the page offset, but the number of bits in the locked bitmap. The page offset is the "heap number" on line 10.

Line 11~15: Display the recorded data in hexadecimal numbers. Field 0 is the cluster index (primary key). Ignore the highest bit, the value is 3. Field 1 is the transaction ID of the transaction that last modified the record, with a decimal value of 2164001, which is transaction (2). Field 2 is the rollback pointer. Starting from field 3 is the remaining row data. Field 3 is an integer column with a value of 8. Field 4 is a string column with the character'c'. By reading the data, we know exactly which row is locked and what the current value is.

What else can we learn from the analysis?

Since most MySQL deadlocks occur between two transactions, we can start the analysis based on this assumption. In example 1, trx(2) is waiting for a shared lock, so trx(1) holds a shared or mutex lock on the primary key record of table t2. Assuming that col2 is a foreign key column, by checking the current statement of trx(1), we know that it does not require the same record lock, so it must be that some previous statement in trx(1) requires S or X lock(s) in On the PK record of t2. Trx(1) only changed 4 lines in 7 seconds. Then, you learned some of the characteristics of trx(1): it did a lot of processing, but made some changes; the changes involved tables t1 and t2, and a single record was inserted into t2. This information combined with other data can help developers find transactions.

Where else can we find previous statements of transactions?

以下是提取事务历史记录的辅助查询,其中<PROCESSID>是有问题的连接的ID。

SELECT * FROM performance_schema.events_statements_history 
WHERE thread_id = (SELECT THREAD_ID FROM performance_schema.threads 
WHERE PROCESSLIST_ID = <PROCESSID>) \G

您可以在此处找到events_statements_history表的更多详细信息。

In addition to the application log and previous SHOW ENGINE INNODB STATUS output, you can also use binlog, slow log and/or regular query log. For binlog, if binlog_format = statement, each binlog event will have thread_id. Only the committed transactions are recorded in binlog, so we can only look up Trx (2) in binlog. In the case of example 1, we know when the deadlock occurred, and we know that Trx(2) started 9 seconds ago. We can run mysqlbinlog on the correct binlog file and look for the statement thread_id = 155097580. Then cross-reference these statements with the application code for confirmation is always a good thing.


$ mysqlbinlog -vvv --start-datetime=“2014-10-13 6:06:12” --
stop-datatime=2014-10-13 6:06:22” 
mysql-bin.000010 > binlog_1013_0606.out

使用Percona Server 5.5及更高版本,您可以设置log_slow_verbosity在慢日志中包括InnoDB事务ID。然后,如果long_query_time = 0,则可以捕获所有语句,包括回滚到慢日志文件中的语句。在常规查询日志中,包含线程ID,该线程ID可用于查找相关语句。

How to avoid MySQL deadlock

Change the application. In some cases, you can greatly reduce the occurrence of deadlocks by dividing a long transaction into smaller transactions, so the lock will be released more quickly. In other cases, the cause of increased deadlock is that two transactions touch the same set of data in one or more tables in a different order. Then change them to access the data in the same order, in other words, serialize the access. In this way, when transactions occur simultaneously, you will have lock waiting instead of deadlock.

Change the table schema, such as removing foreign key constraints to separate the two tables, or adding indexes to minimize scans and locked rows.

In the case of gap locks, you can change the transaction isolation level to read committed sessions or transactions to avoid it. However, the binary log format of the session or transaction must be ROW or MIXED. (RC+row avoid gap lock gap)

文章来自percona官网2014年,需要我们自行测试

This article explains that the main technical content comes from the sharing of Internet technology giants, as well as some self-processing (only for the role of annotations). If related questions, please leave a message after the confirmation, the implementation of infringement will be deleted

Guess you like

Origin blog.csdn.net/baidu_34007305/article/details/111269727