简体   繁体   English

在MySQL InnoDB中获取后续查询的锁定

[英]What locks following queries acquire in MySQL InnoDB

I am trying to investigate deadlock issues in my application. 我正在尝试调查我的应用程序中的死锁问题。 My table looks something like this. 我的表看起来像这样。

CREATE TABLE `requests` (
  `req_id` bigint(20) NOT NULL auto_increment,
  `status` varchar(255) default NULL,
  `process_id` varchar(200) default NULL,
  PRIMARY KEY  (`req_id`),
  KEY `status_idx` USING BTREE (`status`),
  KEY `pk_idx_requests` USING BTREE (`req_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • A Service(multiple threads) issues insert statements on this table. 服务(多个线程)在此表上发出insert语句。
  • Multiple clients issue following queries in order in two separate transactions. 多个客户端在两个单独的事务中按顺序发出以下查询。

    update requests set process_id='" + hostName + "' where status='Received' and process_id is null order by req_id asc limit 100" 更新请求设置process_id ='“+ hostName +”'其中status ='已接收'且process_id为空顺序req_id asc limit 100“

    select * from requests where process_id='"+ hostName + "' where status='Received'; select * from requests where process_id ='“+ hostName +”'where status ='Received';

    update requests set status='Processing' where req_id='xyz' 更新请求设置状态='处理',其中req_id ='xyz'

Req_id in 3rd query is list of req ids retrieved from 2nd query. 第三个查询中的Req_id是从第二个查询中检索到的req id列表。

But on client side some times, we see following exception. 但有时在客户端,我们看到以下异常。

Deadlock found when trying to get lock; try restarting transaction
org.hibernate.exception.LockAcquisitionException: could not execute native bulk manipulation query

Can above queries result in deadlock, if yes how can we resolve it? 以上查询是否会导致死锁,如果是,我们如何解决? Also is there a way to reproduce this issue locally? 还有一种方法可以在本地重现此问题吗?

Here is output of 'show innodb status' 这是'show innodb status'的输出

LATEST DETECTED DEADLOCK
------------------------
120507  6:03:21
*** (1) TRANSACTION:
TRANSACTION 115627, ACTIVE 1 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1248, 25 row lock(s)
MySQL thread id 432399, OS thread handle 0x419e4940, query id 4111695 * * * Searching rows for update
update requests set process_id='**' where status='Received' and process_id is null order by req_id asc limit 100
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 4 page no 3797 n bits 136 index `PRIMARY` of table `db`.`requests` trx id 115627 lock_mode X locks rec but not gap waiting
Record lock, heap no 67 PHYSICAL RECORD: n_fields 27; compact format; info bits 0
*** (2) TRANSACTION:
TRANSACTION 115626, ACTIVE 1 sec updating or deleting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 432403, OS thread handle 0x41c19940, query id 4111694 * * *  Updating
update requests set status='Processing', process_id='**' where req_id=3026296
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 4 page no 3797 n bits 136 index `PRIMARY` of table `db`.`requests` trx id 115626 lock_mode X locks rec but not gap
Record lock, heap no 67 PHYSICAL RECORD: n_fields 27; compact format; info bits 0

Some background 一些背景

MySQL takes write locks out on an UPDATE statement the first time it visits the records. MySQL在第一次访问记录时在UPDATE语句上执行写锁定。 It doesn't elevate locks from read to write. 它不会将锁从读取提升到写入。 It locks based on the current index . 根据当前索引进行锁定

In your UPDATE statement, MySQL is most likely using the index on the status column, so MySQL locks every record where status = 'Received'. 在您的UPDATE语句中,MySQL最有可能使用status列上的索引,因此MySQL会锁定status ='Received'的每条记录。

Note that any time you lock more than a single unique record (using a unique index, such as the primary key), you are locking a gap (or range). 请注意,只要您锁定多个唯一记录(使用唯一索引,例如主键),就会锁定间隙(或范围)。

An update against a single record still takes a next-key lock, which means it locks the selected record and the next one in the index. 针对单个记录的更新仍然采用下一键锁定,这意味着它将锁定所选记录和索引中的下一个记录。

Two UPDATES on the same index (both with a next-key) lock won't conflict (they'll always be locked in the same order). 同一索引上的两个UPDATES(都带有下一个键)锁定不会发生冲突(它们将始终以相同的顺序锁定)。 However, since your range lock is against a secondary index, it could deadlock. 但是,由于您的范围锁定是针对二级索引,因此它可能会死锁。

Here's the scenario that is occurring : 这是正在发生的情况

Let's say you have two records with req_ids 1 and 2. 假设您有两条带有req_ids 1和2的记录。

Your first transaction updates against the status index and needs to lock both records 1 and 2, but it's not guaranteed to be in the same order as the primary key, so it locks record 2, and is about to lock record 1. 您的第一个事务更新状态索引并需要锁定记录1和2,但不能保证与主键的顺序相同,因此它会锁定记录2,并且即将锁定记录1。

Your second transaction locks on the req_id index, and needs to update record 1. It immediately locks record 1, but it also needs to perform a next-key lock on record 2. 您的第二个事务锁定req_id索引,并需要更新记录1.它立即锁定记录1,但它还需要在记录2上执行下一键锁定。

The two transactions are now deadlocked. 这两笔交易现已陷入僵局。 Transaction 1 needs to lock record 1, and transaction 2 needs to lock record 2. 事务1需要锁定记录1,事务2需要锁定记录2。

The solution 解决方案

To avoid deadlocks in your case, you could explicitly lock the entire table using LOCK TABLES , or simply retry a transaction if it fails. 为了避免在您的情况下出现死锁,您可以使用LOCK TABLES显式锁定整个表,或者只是在失败时重试事务。 MySQL will detect the deadlock, and one of your transactions will get rolled back. MySQL将检测死锁,您的一个事务将被回滚。

MySQL does provide some instructions to help you cope with deadlocks . MySQL确实提供了一些指导来帮助您应对死锁

It also seems like you should delete the redundant key pk_idx_requests, since your primary key already includes that column. 您似乎应该删除冗余密钥pk_idx_requests,因为您的主键已包含该列。

Yes, these queries could result in a deadlock. 是的,这些查询可能会导致死锁。 In fact, as mentioned in the MySQL doco, you can get deadlocks even in the case of transactions that just insert or delete a single row. 实际上,正如MySQL doco中所提到的,即使在只插入或删除单行的事务中,也可能出现死锁。 See How to Cope with Deadlocks 请参阅如何应对死锁

You could try indexing process_id to try to speed up the queries/updates. 您可以尝试索引process_id以尝试加快查询/更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM