Почему одновременные операторы «Удалить ... Вставить» вызывают тупик? - PullRequest
0 голосов
/ 22 октября 2019

Рассмотрим следующую схему в mysql:

create table foo(
  id int not null primary key auto_increment,
  name varchar(32) not null,
  unique key(name)
);

И в таблице есть запись с именем "abc".

У меня есть транзакция (RC):

start transaction;
delete from foo where name = "abc";
insert into foo(name) values("abc");
commit;

Если есть две параллельные транзакции, будет иметь место мертвая блокировка.

       |        TX A         |             TX B
---------------------------------------------------------------------
Step 1 | start transaction;  | 
       | delete name="abc";  |
---------------------------------------------------------------------
Step 2 |                     | start transaction;
       |                     | delete name="abc";
       |                     | <wait for lock>
---------------------------------------------------------------------
Step 3 | insert name="abc";  | <deadlock detected, exit>
---------------------------------------------------------------------
Step 4 | commit;             |
---------------------------------------------------------------------

Мне интересно, почему эта последовательность вызывает тупик.

В документе mysql сказано(https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html)

Если возникает ошибка с дублирующимся ключом, устанавливается общая блокировка для записи дублирующегося индекса. Такое использование общей блокировки может привести к взаимоблокировке, если существует несколько сеансов, пытающихся вставитьта же строка, если другой сеанс уже имеет монопольную блокировку. Это может произойти, если другой сеанс удаляет строку.

Я полагаю, что когда транзакция A выполняет оператор "delete", она получает блокировку Xзапись "abc". Когда выполняется оператор "insert", он пытается получить блокировку S из-за "ошибки дублированного ключа". Разве он не должен получить блокировку S, поскольку он получил блокировку X той же записи? Почему тупик случаетсяздесь?

1 Ответ

0 голосов
/ 22 октября 2019

Я воспроизвел тупик и получил журнал состояния innoDB следующим образом:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-10-18 18:35:14 0x7f1dfc738700
*** (1) TRANSACTION:
TRANSACTION 26547965, ACTIVE 6 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
/* ApplicationName=DataGrip 2019.1.1 */ delete from foo where name='abc'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3011 page no 4 n bits 224 index IDX_NAME of table `foo` trx id 26547965 lock_mode X locks rec but not gap waiting
Record lock, heap no 153 PHYSICAL RECORD: n_fields 2; ....

*** (2) TRANSACTION:
TRANSACTION 26547960, ACTIVE 10 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
/* ApplicationName=DataGrip 2019.1.1 */ INSERT INTO foo(id, name)
VALUES (1, 'abc')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 3011 page no 4 n bits 224 index IDX_NAME of table `foo` trx id 26547960 lock_mode X locks rec but not gap
Record lock, heap no 153 PHYSICAL RECORD: ...

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3011 page no 4 n bits 224 index IDX_NAME of table `foo` trx id 26547960 lock mode S waiting
Record lock, heap no 153 PHYSICAL RECORD: ....

*** WE ROLL BACK TRANSACTION (1)

В журнале четко объясняется причина: TX B ожидает X блокировки, удерживаемой TX A, в то же время TX Aожидание блокировки S, которая блокируется запросом блокировки TX B.

В соответствии с Mysql doc:

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

Оператор вставки в какой-то момент получает блокировку S, поэтому причина возникновения тупика очень ясна.

Но проблема в следующем:

  • в соответствии с документом mysql, оператор вставки получит блокировку S, если произойдет ошибка с повторяющимся ключом, чего не происходит в текущем случае, мы обсуждаем
  • , почему оператор вставки все еще получает блокировку Sкогда текущая транзакция уже содержит X-блокировку, X-блокировки достаточно для выполнения текущего чтения, чтобы проверить ошибку дублирующего ключа. Так для чего она используется?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...