Как объяснить причину этого тупика? - PullRequest
1 голос
/ 05 июля 2019

Есть транзакция буксировки, транзакция 1 удерживает S-блокировку в строке, транзакция 2 хочет обновить строку, затем транзакция 2 ожидает, затем транзакция 1 также выполняет обновления строки, в это время возникает тупик, яподумать Знаешь, в чем причина?Какова ситуация блокировки здесь?

Я выполнил следующий тест на версии mysql5.6. Есть тупик.

Структура таблицы:

CREATE TABLE `test` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增',
  `uni_id` bigint(20) DEFAULT NULL,
  `current_status` int(11) DEFAULT '0' ,
  `total` int(11) NOT NULL DEFAULT '0' ,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_id_unique` (`uni_id`),
  KEY `uni_id_idx` (`uni_id`),
  KEY `current_status_idx` (`current_status`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

данные инициализации:

INSERT INTO `test`(`id`, `uni_id`, `current_status`, `total`) VALUES (1, 1, 0, 1);

Следующие операции выполняются в следующем порядке: 1. Первый шаг Транзакция 1:

 start transaction;
 select * from test where id=1 lock in share mode;
второй шаг
start transaction;
update test set uni_id=1,total=total+1 where uni_id=1;
третий шаг Транзакция 1:
update test set current_status=1 where id=1 and 
current_status=0;

, затем произошла разблокировка.

  1. первый шаг: транзакция 1 удерживает блокировку S.
  2. секундашаг: транзакция 2 ожидает, и по результатам отладки исходного кода полученная блокировка не удалась.
  3. третий шаг: тупик

информация о взаимоблокировке:

*** (1) TRANSACTION:
TRANSACTION 4360, ACTIVE 14 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 376, 2 row lock(s)
MySQL thread id 2, OS thread handle 0x70000a7f4000, query id 145 localhost 127.0.0.1 root updating
update test set uni_id=1,total=total+1 where uni_id=1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 4 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 4360 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 8; hex 8000000000000001; asc         ;;
 1: len 6; hex 000000001106; asc       ;;
 2: len 7; hex 83000001360110; asc     6  ;;
 3: len 8; hex 8000000000000001; asc         ;;
 4: len 4; hex 80000000; asc     ;;
 5: len 4; hex 80000001; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 4359, ACTIVE 24 sec starting index read
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 1, OS thread handle 0x70000a7b0000, query id 149 localhost 127.0.0.1 root updating
update test set current_status=1 where id=1 and 
current_status=0
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 4 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 4359 lock mode S locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 8; hex 8000000000000001; asc         ;;
 1: len 6; hex 000000001106; asc       ;;
 2: len 7; hex 83000001360110; asc     6  ;;
 3: len 8; hex 8000000000000001; asc         ;;
 4: len 4; hex 80000000; asc     ;;
 5: len 4; hex 80000001; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 4 page no 3 n bits 72 index `PRIMARY` of table `test`.`test` trx id 4359 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 8; hex 8000000000000001; asc         ;;
 1: len 6; hex 000000001106; asc       ;;
 2: len 7; hex 83000001360110; asc     6  ;;
 3: len 8; hex 8000000000000001; asc         ;;
 4: len 4; hex 80000000; asc     ;;
 5: len 4; hex 80000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

Ответы [ 2 ]

1 голос
/ 05 июля 2019

Я не верю, что ваш анализ того, что на самом деле произошло, абсолютно верен.Это вероятная версия событий:

  1. Первая транзакция получает S-блокировку для записи
  2. Вторая транзакция хочет получить эксклюзивную блокировку для той же записи, но не может,потому что первая транзакция содержит блокировку S.Поэтому эта транзакция ожидает, пытаясь получить блокировку.
  3. Третья транзакция также переходит в состояние ожидания для той же записи, но теперь происходит взаимоблокировка.

Из MySQL документация :

Здесь FOR SHARE не является хорошим решением, поскольку, если два пользователя одновременно читают счетчик, по крайней мере один из них оказывается в тупике при попыткеобновите счетчик.

Как следует из документации, лучшим подходом может быть SELECT ... FOR UPDATE:

SELECT * FROM test WHERE id = 1 FOR UPDATE;
UPDATE test SET uni_id = 1, total = total+1 WHERE uni_id = 1;
0 голосов
/ 25 июля 2019

Мой друг объяснил эту ситуацию.

Из документации MYSQL:

Здесь возникает тупик, потому что клиент A нуждается в блокировке X для удаления строки. Однако этот запрос блокировки не может быть удовлетворен, потому что клиент B уже имеет запрос на блокировку X и ожидает, пока клиент A освободит свою блокировку S. Также S-блокировка, удерживаемая A, не может быть преобразована в X-блокировку из-за предварительного запроса B о блокировке X. В результате InnoDB генерирует ошибку для одного из клиентов и снимает свои блокировки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...