MySQL 5.7 - тупик при откате, но не при фиксации, при использовании ON DUPLICATE KEY

0

Предпосылки: У меня есть приложение, которое имеет много параллельных потоков. В одном месте я хочу обновить определенную строку базы данных, но я не могу быть уверен, что строка действительно существует. Следовательно, мне нужно либо создать строку ИЛИ обновить строку, если она существует. Но при этом я столкнулся с проблемой, которую я здесь представляю.

Используя MySQL 5.7, я столкнулся с цепочкой событий, которая вызывает тупик, но я не могу понять, почему. Используя три разных клиента (c1, c2 и c3), мы выполняем следующую цепочку запросов:

c1 > BEGIN;
c1 > INSERT INTO 'user_points' (userid, points) VALUES (1,1) 
     ON DUPLICATE KEY UPDATE points = points + 1;

c2 > INSERT INTO 'user_points' (userid, points) VALUES (1,1) 
     ON DUPLICATE KEY UPDATE points = points + 1;

c3 > INSERT INTO 'user_points' (userid, points) VALUES (1,1) 
     ON DUPLICATE KEY UPDATE points = points + 1;

c1 > ROLLBACK;

Эта серия запросов приведет к тому, что c3 получит ошибку тупика, а c2 выполнит свой запрос без ошибок. Однако, если последний запрос c1 является фиксацией (вместо отката), то эта серия работает так, как ожидалось.

Вот таблица, которую я использую:

CREATE TABLE 'points' (
    'userid' int(11) unsigned NOT NULL,
    'points' int(11) unsigned NOT NULL,
PRIMARY KEY ('userid')
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

И вот тупик, о котором сообщает движок InnoDB:

------------------------
LATEST DETECTED DEADLOCK
------------------------
180802  8:48:02
*** (1) TRANSACTION:
TRANSACTION 1D69A6, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 135, OS thread handle 0x7f3c9829b700, query id 12166 172.30.0.1 root update
INSERT INTO points (userid, points) VALUES (1,0) ON DUPLICATE KEY UPDATE points = points + 1

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 55819 n bits 72 index 'PRIMARY' of table 'temp'.'points' trx id 1D69A6 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) TRANSACTION:
TRANSACTION 1D69A7, ACTIVE 1 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1248, 2 row lock(s)
MySQL thread id 137, OS thread handle 0x7f3c98239700, query id 12167 172.30.0.1 root update
INSERT INTO points (userid, points) VALUES (1,0) ON DUPLICATE KEY UPDATE points = points + 1

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 55819 n bits 72 index 'PRIMARY' of table 'temp'.'points' trx id 1D69A7 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 55819 n bits 72 index 'PRIMARY' of table 'temp'.'points' trx id 1D69A7 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (2)

Я немного искал язык и нашел аналогичный вопрос: почему commit не вызывает тупик. Но не будет ли использовать ON DUPLICATE KEY обойти проблему, описанную в этом вопросе? На странице ссылок MySQL я прочитал следующее:

INSERT... ON DUPLICATE KEY UPDATE отличается от простого INSERT тем, что в строке, которая должна быть обновлена при возникновении ошибки с дублирующимся ключом, помещается эксклюзивная блокировка, а не общая блокировка. Для дублирования значения первичного ключа берется эксклюзивная блокировка индекса. Для дублирования уникального значения ключа берется эксклюзивная блокировка следующего ключа.

То, что я пытаюсь сделать, выглядит как довольно простая операция: создайте строку, если она не существует, и обновите ее, если она уже выполняется. Как может быть, что это не работает? Что на самом деле происходит здесь? И почему я получаю тупик при откате, а не фиксации?

  • 0
    Дополнительные блокировки вступают в действие только тогда, когда возникает ошибка дублирующего ключа . Ошибка на самом деле не происходит, потому что тупик происходит на insert -part - точно такая же ситуация, как и в вопросе, который вы связали. Не уверен, что вы согласны с тем, что в случае «только для вставки» возникает тупик, но если вы это сделаете, это может быть недостающим фрагментом головоломки.
  • 0
    Я подумал, что между этими двумя процедурами есть какая-то разница, потому что если вы выполняете серию запросов из связанного вопроса, блокировка, удерживаемая транзакцией 2, является блокировкой S, в то время как блокировка, удерживаемая транзакцией 2 в этом вопросе, является блокировкой X ,
Показать ещё 1 комментарий
Теги:
database
innodb
database-deadlocks

1 ответ

0

Цели InnoDB

  1. Целостность данных.
  2. Быстро.
  3. Минимизировать ложные блокировки и т.д. Для краевых случаев.

Вы бы отказались от № 1? Я сомневаюсь.
Вы бы расслабились? Может быть. Но это решение не было для этого случая.

Не стесняйтесь сообщать об ошибке на bugs.mysql.com; возможно, есть исправление, которое не ставит под угрозу № 1 и не замедляет работу слишком ".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню