SQLSTATE [40001]: Ошибка сериализации: 1213 Проблема взаимоблокировки, вызванная триггером INSERT при одновременном доступе - PullRequest
0 голосов
/ 31 октября 2019

Я обнаружил проблему взаимоблокировки SQL, которая возникает, когда функция выполняется одновременно двумя пользователями. У меня есть функция PHP, которая выполняет несколько запросов на вставку базы данных, которые заключают в транзакции. И одна из вставок также запускает курок. См. Мою схему таблицы и пример кода ниже.

main_table

CREATE TABLE `main_table` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `action_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

history_table

CREATE TABLE `history_table` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `action_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  `audit_id` INT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

aud_table

CREATE TABLE `audit_table` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `action_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

У меня есть триггер на main_table , определенный следующим образом. То, что он делает, - это выбор максимального идентификатора из audit_table и вставка записи в history_table .

CREATE TRIGGER watch_insert_main_table
AFTER INSERT ON main_table
FOR EACH ROW BEGIN
    DECLARE max_id INT;
    SET max_id = (SELECT MAX(`id`) FROM `audit_table`) + 1;
    INSERT INTO `history_table` SET audit_id=max_id;
END;

Ниже приведена функция, которая выполняется двумя пользователямиодновременно. insertRecord Функция просто вставляет запись в данную таблицу.

try {
    $dbConnection->beginTransaction();
    $this->insertRecord('main_table');    
    $this->insertRecord('audit_table');
    $dbConnection->commit();    
} catch (Exception $e) {
    $dbConnection->rollback();    
}

Я получаю следующую ошибку мертвой блокировки, когда функция вызывается во второй раз (одновременно).

40001 - SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction

Если я внесу одно из следующих изменений, эта проблема не возникнет.

  • Удалите вложенную транзакцию в коде PHP
  • Удалите $ this-> insertRecord ('audit_table'); строка из кода PHP
  • Измените триггер так, чтобы в нем не было оператора select из aud_table

Я хочузнать коренную причину этой проблемы . Другая транзакция запускается из триггера MySQL при запуске? Как транзакции и блокировки работают внутри триггера?

Я обнаружил, что следующие два вопроса также связаны с похожей проблемой.

1 Ответ

0 голосов
/ 05 ноября 2019

Посмотрите, поможет ли это упрощение:

CREATE TRIGGER watch_insert_main_table
AFTER INSERT ON main_table
FOR EACH ROW BEGIN
    INSERT INTO `history_table` (audit_id)
        SELECT MAX(`id`)+1 FROM `audit_table`;
END;

Обратите внимание, как оно объединяет ваши два утверждения в одно утверждение. Это может не позволять другому соединению входить туда и захватывать то же самое id (или что-то в этом роде).

Следующее может быть связанным (предоставленоOP): это происходит из-за известной проблемы в MySQL. Установка переменной из выбора получает блокировку при использовании уровня изоляции незафиксированного чтения. В этой теме содержится дополнительная информация.

Используете ли вы режим изоляции Read Uncommitted? Если так, то почему?

...