Попытка понять тупик MySQL на таблице InnoDB

Я пытаюсь понять тупик, обнаруженный MySQL при обработке параллельных клиентов в тех же таблицах. Это интересная часть команды "SHOW InnoDB STATUS":

------------------------
LATEST DETECTED DEADLOCK
------------------------
120704 16:17:51
*** (1) TRANSACTION:
TRANSACTION 0 3547576, ACTIVE 0 sec, process no 10886, OS thread id 140547111458560 inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 368, 1 row lock(s), undo log entries 1
MySQL thread id 41941, query id 1725666 localhost testsuite update
insert into `INode` (`status`,`_type`,`group`,`ctime`,`parent`,`shared`,`basename`,`_rowid`,`displayname`,`user`,`content_type`,`mtime`,`position`,`atime`,`size`) values ('Published','Group','12','2012-07-04 16:17:48.996869','2',null,'1','12','1','3','application/x-empty','2012-07-04 16:17:48.996896','1','2012-07-04 16:17:48.996914',null)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 5554 n bits 80 index `INodeparent_basename` of table `testsuite`.`INode` trx id 0 3547576 lock mode S waiting
Record lock, heap no 12 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000002; asc     ;; 1: len 1; hex 31; asc 1;; 2: len 4; hex 8000000b; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 0 3547575, ACTIVE 0 sec, process no 10886, OS thread id 140547107845888 inserting, thread declared inside InnoDB 493
mysql tables in use 1, locked 1
13 lock struct(s), heap size 3024, 17 row lock(s), undo log entries 21
MySQL thread id 41940, query id 1725808 localhost testsuite update
replace into `INode` (`status`,`_type`,`position`,`group`,`ctime`,`parent`,`basename`,`_rowid`,`displayname`,`user`,`content_type`,`mtime`,`shared`,`atime`,`size`) values ('Published','Group','0','2','2012-07-04 16:17:49','1','groups','2','admin','3','application/x-empty','2012-07-04 16:17:49',null,'2012-07-04 16:17:49',null),('Published','Group','1','11','2012-07-04 16:17:51.064074','2','1','11','1','3','inode/directory','2012-07-04 16:17:51.064074',null,'2012-07-04 16:17:51.064074',null)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 5554 n bits 80 index `INodeparent_basename` of table `testsuite`.`INode` trx id 0 3547575 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000001; asc     ;; 1: len 6; hex 67726f757073; asc groups;; 2: len 4; hex 80000002; asc     ;;

Record lock, heap no 12 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000002; asc     ;; 1: len 1; hex 31; asc 1;; 2: len 4; hex 8000000b; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 5554 n bits 80 index `INodeparent_basename` of table `testsuite`.`INode` trx id 0 3547575 lock_mode X waiting
Record lock, heap no 12 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000002; asc     ;; 1: len 1; hex 31; asc 1;; 2: len 4; hex 8000000b; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

Из этого журнала я понимаю, что транзакция (1) ожидает получения общей блокировки индекса INodeparent_basename таблицы testsuite. INode. Теперь транзакция (2) имеет исключительную блокировку по этому же самому индексу. Но странно, что транзакция (2) также ждет блокировки X по данной таблице. Почему это? Если транзакция (2) уже имеет блокировку, почему она ее ждет?

(в общем, я не нашел никакой документации, которая точно объясняет, как читать вывод команды status - было бы интересно, если бы кто-нибудь мог указать на это и мне)

Ответ 1

В этом случае транзакция 2 приобрела общую блокировку для предыдущего оператора. (* (2) ОТВЕРСТИЕТ БЛОКИРОВКУ (S):)

Затем транзакция 1 попыталась получить эксклюзивную блокировку в той же строке и ожидает, что блокировка общего доступа будет удалена.

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

Ответ 2

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

Таблица 1 Таблица 2

а в другом -

Таблица 2 Таблица 1 (только пример)

Не видя фактического кода, я предлагаю изучить такие вещи и убедиться, что вы делаете их в той же последовательности работы/обновления/вставки записей, чтобы все они работали как-то вроде

вставить/обновить из таблицы 1 затем вставьте/обновите таблицу 2

Таким образом, первая блокировка всегда выполняется в первой таблице 1. Если это не удастся, тогда она даже не пробует таблицу 2 до тех пор, пока не будет выпущена таблица 1.

Затем примените все изменения к таблице 2 и завершите транзакцию. Когда обе таблицы 2 и 1 затем освобождаются, следующая транзакция, ожидающая освобождения в таблице 1, может продолжить.