Позвольте мне начать свой вопрос с примера SQL...
Вот настройка таблицы,
START TRANSACTION;
CREATE TABLE 'x' (
'id' INT(11) NOT NULL AUTO_INCREMENT,
'value' INT(11) NOT NULL,
PRIMARY KEY ('id')
) ENGINE=INNODB;
CREATE TABLE 'y' (
'id' INT(11) NOT NULL AUTO_INCREMENT,
'x_id' INT(11) NOT NULL,
'value' INT(11) NOT NULL,
PRIMARY KEY ('id'),
CONSTRAINT 'fk_x' FOREIGN KEY ('x_id')
REFERENCES 'x' ('id')
) ENGINE=INNODB;
INSERT INTO x values (1,123456);
COMMIT;
Теперь запустите транзакцию (Trx A), чтобы обновить строку в X
START TRANSACTION;
UPDATE x SET value=value+1 WHERE id = 1;
Прежде чем он будет зафиксирован, я начинаю новую транзакцию (Trx B), чтобы вставить строку в Y
START TRANSACTION;
INSERT INTO y VALUES (null,1,123456);
---- HANGED ----
-- Until Trx A is committed or rolled-back, the Trx B is hanged here.
Вопрос в том, ожидалось ли, что Trx B будет повешен в этой точке? Почему и каким-то образом обходным путем?
Это было протестировано на MySQL 5.7.21, Precona 5.7.21, Mariadb 10.2.14
Да, ожидается.
Trx A имеет исключительную блокировку (X) в записи, потому что она обновляет ее.
Trx B должен получить блокировку общего режима (S) для всех внешних ключей, чтобы гарантировать, что ограничения выполнены. Он ожидает, что Trx A освободит свой X-замок.
Невозможно избежать этого и сохранить ссылочную целостность. Если вам удастся отключить блокировку, MySQL не сможет гарантировать, что указанная строка существует.
Обычным обходным решением является удаление внешних ключей.