Как предотвратить эту тупиковую ситуацию с помощью операторов INSERT и UPDATE внутри транзакции - PullRequest
0 голосов
/ 10 мая 2018

У меня есть две таблицы table_a и table_b. В моем приложении у меня есть бизнес-метод, который выполняет следующие действия:

// it is one transaction

INSERT INTO table_b (join_col) VALUES (some_value);

UPDATE table_a a 
JOIN table_b b ON a.join_col = b.join_col AND a.column_a = b.column_b
SET a.column_c = a.column_c + 1;

Я использую движок InnoDB, и проблема в том, что когда я запускаю свой метод параллельно или почти параллельно, я часто получаю следующее сообщение об ошибке:

Обнаружен тупик при попытке получить блокировку; попробуйте перезапустить транзакцию

Похоже, первая транзакция начала обновлять table_a и поставить блокировку на table_b, тогда как вторая параллельная транзакция не может быть вставлена ​​в table_b, потому что она заблокирована.

Если я прокомментирую мой UPDATE statemenmt, он начнет работать. Обе транзакции вставляются в table_b без проблем. Итак, как я могу это исправить? Если это имеет значение, я использую MySQL 5.7.

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

Это похоже на странный код.

  1. Вставить набор значений в B.
  2. Используйте все эти значения для обновления другой таблицы.

Ну, а если вы играете с тем же B в другом потоке, что вы ожидаете? Вы не хотите, чтобы список вещей в B менялся.

Рассмотрите возможность использования TEMPORARY TABLE для B ??

0 голосов
/ 10 мая 2018

Похоже, вы блокируете таблицу b, а затем таблицу a. Вероятно, блокировка является блокировкой индекса.

Попробуйте, чтобы всегда сначала захватить соответствующую блокировку на столе.

BEGIN TRANSACTION;
  SELECT COUNT(join_col) INTO @counter 
    FROM table_a a
   WHERE a.join_col = some_value
     FOR UPDATE;
  INSERT INTO table_b (join_col) VALUES (some_value);
  UPDATE table_a a 
    JOIN table_b b ON a.join_col = b.join_col 
                  AND a.column_a = b.column_b
     SET a.column_c = a.column_c + 1;
COMMIT;

Материал INTO @counter не позволяет первому SELECT вернуть набор результатов в вашу программу.

Если это не сработает, попробуйте использовать LOCK TABLES . Это большой молоток, но эффективный.

LOCK TABLES table_a, table_b WRITE;
 your queries
UNLOCK TABLES;
...