Подтвердить что ты не робот

Вопрос о тупиковой ситуации, когда транзакция пытается получить блокировку, которую она уже удерживает

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

Происходят две транзакции:
(2) содержит блокировку для запроса delete from myTable where id = NAME_CONST('p_id',10000). Это блокировка PRIMARY KEY, но не полный ключ, а диапазон. Похоже, это полный замок записи, когда он говорит lock_mode X locks rec but not gap.
(1) ждет эту же блокировку, также для запроса delete from myTable where id = NAME_CONST('p_id',10000).
(2) также пытается получить эту блокировку, и MySQL обнаруживает тупик.

То, что я не могу понять, - это то, почему (2) необходимо снова получить блокировку, поскольку она уже удерживает ее, и во всех случаях она блокирует запись (lock_mode X).

Он также похож на тот же самый запрос.

Вот определение таблицы

create myTable (
  id int unsigned not null,
  value1 char(8) not null,
  value2 int unsigned,
  primary key (id, value1)
);

и вот информация из SHOW ENGINE INNODB STATUS\G

------------------------
LATEST DETECTED DEADLOCK
------------------------
130313 14:46:28
*** (1) TRANSACTION:
TRANSACTION 75ACB8A3, ACTIVE 0 sec, process no 6110, OS thread id 139973945382656 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 5154970, query id 5201313618 192.168.0.2 user updating
delete from myTable where id = NAME_CONST('p_id',10000)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB8A3 lock_mode X waiting
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 0005af3a; asc :;;
1: len 8; hex 2020202020202020; asc ;;
2: len 6; hex 000075acb890; asc u ;;
3: len 7; hex ea0000020d011e; asc ;;
4: len 4; hex 00000065; asc e;;

*** (2) TRANSACTION:
TRANSACTION 75ACB890, ACTIVE 0 sec, process no 6110, OS thread id 139973957895936 starting index read
mysql tables in use 1, locked 1
7 lock struct(s), hea
p size 1248, 6 row lock(s), undo log entries 4
MySQL thread id 5155967, query id 5201313625 192.168.0.1 user updating
delete from myTable where id = NAME_CONST('p_id',10000)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB890 lock_mode X locks rec but not gap
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 0005af3a; asc :;;
1: len 8; hex 2020202020202020; asc ;;
2: len 6; hex 000075acb890; asc u ;;
3: len 7; hex ea0000020d011e; asc ;;
4: len 4; hex 00000065; asc e;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 22371 page no 1598 n bits 104 index `PRIMARY` of table `db`.`myTable` trx id 75ACB890 lock_mode X waiting
Record lock, heap no 32 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
0: len 4; hex 0005af3a; asc :;;
1: len 8; hex 2020202020202020; asc ;;
2: len 6; hex 000075acb890; asc u ;;
3: len 7; hex ea0000020d011e; asc ;;
4: len 4; hex 00000065; asc e;;

*** WE ROLL BACK TRANSACTION (1)
4b9b3361

Ответ 1

Это не тот же замок - транзакция блокировки 1 имеет только запись (индекс), а не блокировку зазора.

Вот что происходит:

  • Transaction 2 получает блокировку для записи (индекса), но не разрыв перед записью ( "rec, но не разрыв" ), то есть он имеет только блокировку записи.
  • Транзакция 1 пытается получить блокировку записи и промежуток до (например, блокировку следующего ключа), но не может, поскольку транзакция 2 имеет блокировку записи (и поэтому транзакция 1 ждет).
  • Transaction 2 пытается получить блокировку записи и пробел до (т.е. блокировку следующего ключа) и не может, потому что Transaction 1 уже ждет одну и ту же блокировку и опережает ее в очереди.
  • Тупик.

Я не совсем уверен, почему Transaction 2 не сразу получает блокировку следующего ключа - возможно, процесс получения блокировки записи, а затем блокировка блокировки не является атомарной (в общем смысле слова).

Я думаю, проблема заключается в том, что у вас есть составной первичный ключ (id, value1), но вы удаляете его из диапазона (указав только идентификатор) - для этого требуются блокировки пробелов. См. http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html, в частности:

Блокировка блокировки не требуется для операторов, которые блокируют строки, используя уникальную index для поиска уникальной строки. (Это не включает случай, когда условие поиска включает только некоторые столбцы из нескольких столбцов уникальный индекс; в этом случае происходит блокировка зазора.)

Можете ли вы изменить свой код, чтобы указать полный первичный ключ при удалении, то есть идентификатор и значение1?

Другие параметры:

  • Повторите попытку удаления, когда есть тупик, например. поймать ошибку в вашем коде, а затем повторить попытку, если это было вызвано тупиком. Этот подход часто легче сказать, чем делать, особенно в устаревших приложениях, но рекомендуется на странице MySQL как справляться с взаимоблокировками:

Всегда будьте готовы повторно выпустить транзакцию, если она не удалась из-за тупиковый. Тупики не опасны. Попробуйте еще раз.

  • Заблокировать всю таблицу с блокировкой на уровне таблицы перед выдачей инструкции delete. Это может повлиять на производительность, хотя и является "кувалдой".