Недавно я столкнулся с тупиком при удалении записей (обратите внимание, что уровень изоляции: REPEATABLE READ , MySQL 5.7)
Вот шаги воспроизведения
1 Создать новую таблицу
CREATE TABLE `t` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `p_name` (`name`)
) ENGINE=InnoDB CHARSET=utf8;
2 Подготовить 3 записи
insert into t (name) value ('A'), ('C'), ('D');
3
+====================================+============================================================+
| Session A | Session B |
+====================================+============================================================+
| begin; | |
+------------------------------------+------------------------------------------------------------+
| | begin; |
+------------------------------------+------------------------------------------------------------+
| delete from t where name = 'C'; | |
+------------------------------------+------------------------------------------------------------+
| | delete from t where name = 'C'; --Blocked! |
+------------------------------------+------------------------------------------------------------+
| insert into t (name) values ('B'); | |
+------------------------------------+------------------------------------------------------------+
| | ERROR 1213 (40001): Deadlock found when trying to get lock |
+------------------------------------+------------------------------------------------------------+
Результат показать статус innodb движка , как показано ниже (секция ПОСЛЕДНИЕ ОБНАРУЖЕННАЯ БЛОКИРОВКА)
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
TRANSACTION 3631, ACTIVE 21 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 13, OS thread handle 123145439432704, query id 306 localhost root updating
delete from t where name = 'C'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 69 page no 4 n bits 72 index p_name of table `jacky`.`t` trx id 3631 lock_mode X waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 1; hex 43; asc C;;
1: len 8; hex 8000000000000018; asc ;;
*** (2) TRANSACTION:
TRANSACTION 3630, ACTIVE 29 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 14, OS thread handle 123145439711232, query id 307 localhost root update
insert into t (name) values ('B')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 69 page no 4 n bits 72 index p_name of table `jacky`.`t` trx id 3630 lock_mode X
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 1; hex 43; asc C;;
1: len 8; hex 8000000000000018; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 69 page no 4 n bits 72 index p_name of table `jacky`.`t` trx id 3630 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
0: len 1; hex 43; asc C;;
1: len 8; hex 8000000000000018; asc ;;
Как показано в статусе Innodb, сеанс B ожидает блокировки следующего ключа C, а сеанс A удерживает блокировку записи C и ожидает блокировку пропусков C;
Как мы все знаем,
DELETE FROM ... WHERE ... устанавливает исключительную блокировку следующего ключа для каждой записи, с которой встречается поиск
Блокировка следующей клавиши - это комбинация блокировки записи в индексной записи и блокировки промежутка в промежутке перед индексной записью.
Q1 : Я думаю, если сеанс B сначала получил блокировку промежутка (часть следующего ключа), а затем ожидал блокировки записи. Таким образом, последняя вставка в сеансе A была заблокирована сеансом B (из-за блокировки промежутка) и в конечном итоге привела к мертвой блокировке. Правильно?
Q2 : Поскольку C удаляется из индекса, удерживается ли блокировка пропусков сеансом B ('A', 'D ')? Если да, то почему сеанс A ожидает блокировки интенсификации вставки в диапазоне (, 'C')?
Q3 : почему сеанс B имеет 1 row lock(s)
, а сеанс A имеет 4 row lock(s)
?
Q4 : при изменении индекса p_name
на уникальный индекс мы все равно получаем тупик из-за блокировки пробела, это странно. Его поведение отличается от официального do c, в котором говорится, что требуется только блокировка записи.
DELETE FROM ... WHERE ... устанавливает исключительную блокировку следующего ключа для каждого записывать поисковые встречи. Однако требуется только блокировка записи индекса для операторов, которые блокируют строки с использованием уникального индекса для поиска уникальной строки .
Однако это нормально при использовании первичного ключа id
для выполнения удаления (шаги, указанные ниже). Это ошибка в MySQL?
1 Подготовьте данные
delete from t;
insert into t (id, name) value (1, 'A'), (3, 'C'), (5, 'D');
2
+-------------------------------------------+--------------------------------------+
| Session A | Session B |
+-------------------------------------------+--------------------------------------+
| begin; | |
| | begin; |
| delete from t where id = 3; | |
| | delete from t where id = 3; Blocked! |
| insert into t (id, name) values (2, 'B'); | |
| | |
| commit; | |
+-------------------------------------------+--------------------------------------+