2014-02-02 3 views
5

Иногда я получаю мертвую ошибку блокировки в моем приложении:Как избежать этого тупика mysql?

SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction 

В таблице вопрос выглядит следующим образом:

CREATE TABLE `oauth_access_token` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `access_token` text COLLATE utf8_unicode_ci NOT NULL, 
    `oauth_client_id` int(10) unsigned NOT NULL, 
    `instance_id` int(10) unsigned DEFAULT NULL, 
    `expires` datetime NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `FK_oauth_access_token_instance_idx` (`instance_id`), 
    KEY `FK_oauth_access_token_oauth_client_idx` (`oauth_client_id`), 
    CONSTRAINT `FK_oauth_access_token_instance` FOREIGN KEY (`instance_id`) REFERENCES `instance` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 
    CONSTRAINT `FK_oauth_access_token_oauth_client` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 
) ENGINE=InnoDB AUTO_INCREMENT=248 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

У меня есть сделки, которая выполняет эти запросы, и я считаю, что они вызывают затор:

DELETE oauth_access_token_scope 
FROM oauth_access_token_scope 
INNER JOIN oauth_access_token 
ON (oauth_access_token_scope.oauth_access_token_id = oauth_access_token.id) 
WHERE oauth_access_token.expires <= UTC_TIMESTAMP() 

DELETE FROM oauth_access_token WHERE expires <= UTC_TIMESTAMP() 

DELETE oauth_jti 
FROM oauth_jti 
WHERE oauth_jti.expires <= UTC_TIMESTAMP() 

Если я бег SHOW ENGINE INNODB STATUS, вошедшая информация взаимоблокировки:

------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
2014-02-03 10:36:45 7ffc5ded2700 
*** (1) TRANSACTION: 
TRANSACTION 3228803, ACTIVE 0 sec starting index read 
mysql tables in use 1, locked 1 
LOCK WAIT 4 lock struct(s), heap size 1248, 15 row lock(s) 
MySQL thread id 2692, OS thread handle 0x7ffc5dcca700, query id 36603 localhost 127.0.0.1 root updating 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 20338 page no 3 n bits 88 index `PRIMARY` of table `app`.`oauth_access_token` trx id 3228803 lock_mode X waiting 
*** (2) TRANSACTION: 
TRANSACTION 3228804, ACTIVE 0 sec starting index read 
mysql tables in use 1, locked 1 
4 lock struct(s), heap size 1248, 15 row lock(s) 
MySQL thread id 2693, OS thread handle 0x7ffc5ded2700, query id 36604 localhost 127.0.0.1 root updating 
DELETE FROM oauth_access_token WHERE expires <= UTC_TIMESTAMP() 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 20338 page no 3 n bits 88 index `PRIMARY` of table `app`.`oauth_access_token` trx id 3228804 lock mode S 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 20338 page no 3 n bits 88 index `PRIMARY` of table `app`.`oauth_access_token` trx id 3228804 lock_mode X waiting 
*** WE ROLL BACK TRANSACTION (2) 

Хотя я понимаю теорию возникновения взаимоблокировок, я не знаю, почему это происходит в этом случае. Я думаю, что тупик возникает, когда два одновременных пользовательских запроса заставляют эти операторы выполняться одновременно.

Почему в этом случае возникает тупик? Что можно сделать (помимо повторения транзакции), чтобы сделать его «безопасным», чтобы взаимоблокировки не произошли?

ответ

0

Когда вы добавляете соединение в инструкцию UPDATE/DELETE, вы вызываете общую блокировку объединенной таблицы, которая хранит ее, а другая транзакция пытается получить на ней исключительную блокировку.

Не является oauth_access_token_scope.oauth_access_token_id индексированным внешним ключом, указывающим на oauth_access_token? Вы можете избежать первого заявления DELETE, используя ON DELETE CASCADE.

В вашем примере я не знаю, имеет ли ссылка oauth_jti ссылки на другие таблицы, если это так, следует проверить, может ли использоваться один оператор DELETE, так что порядок блокировок от разных транзакций всегда одно и то же.

Другое решение, не изменяя структуру таблицы: поскольку этот тупик может быть связан с одновременным доступом пользователей, добавление ограничений к запросу на удаление, которое мешает другим пользователям изменять данные друг друга, также позволяет избежать взаимоблокировки. Например, если вы можете добавить разные oauth_access_token.oauth_user_id в предложение WHERE для каждого пользователя, тогда они будут блокировать только свои собственные строки.

Смежные вопросы