MySQL возвращает Deadlock со строкой вставки и FK заблокирован «для обновления» - PullRequest
7 голосов
/ 01 апреля 2010

Я получаю ошибку взаимоблокировки в моей транзакции mysql.

Простой пример моей ситуации:

Thread1 > BEGIN;
Query OK, 0 rows affected (0.00 sec)

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE;
1 row in set (0.00 sec)

Thread2 > BEGIN;
Query OK, 0 rows affected (0.00 sec)

Thread2 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world');
[Hangs]

Thread1 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world2');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

Thread2 >
Query OK, 1 row affected (10.00 sec)

B.AID - ИНОСТРАННЫЙ КЛЮЧ, относящийся к A.ID

Я вижу три решения:

  1. перехватить тупиковую ошибку в коде и повторить запрос.
  2. использовать innodb_locks_unsafe_for_binlog в my.cnf
  3. блокировка (для обновления) таблицы A в Thread2 перед вставкой

Есть ли другие решения?

Ответы [ 3 ]

1 голос
/ 02 апреля 2010

Я не знаю, какой код превосходит эти примеры, но, возможно, стоит использовать LOCK IN SHARE MODE для обоих потоков, так как вы на самом деле не обновляете саму строку. Если вы должны использовать LOCK FOR UPDATE, я думаю, что блокировка другого потока будет единственным логическим путем.

Также, если вы откроете путь отходу от MySQL, я обнаружил, что PostgreSQL имеет гораздо лучшее разрешение взаимоблокировок. В некоторых случаях я обнаруживал, что MySQL блокировался каждый раз, когда выполнялся один и тот же сценарий в потоке> 1. Где один и тот же скрипт в PostgreSQL мог бы справиться с этим просто для любого количества параллельных потоков.

0 голосов
/ 11 июня 2011

Здесь нет тупиков, какую версию MySQL и какой уровень изоляции вы используете? Я получил эти результаты, добавив столбец отметки времени в таблицу B:

Thread1 > BEGIN;

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE;
/* 0 rows affected, 1 rows found */

Thread2 > BEGIN;

Thread2 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world', NOW());
[Hangs]

-- after 5 seconds

Thread1 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world2', NOW());
/* 1 rows affected, 0 rows found */

Thread1 > COMMIT;

Thread2 > COMMIT;

B будет содержать 2 строки, которые выглядят так:

  1. 1000 «Hello world» 2011-06-11 19: 23: 15 '
  2. 1000 'Hello world2' '2011-06-11 19: 23: 20'

Описанная вами ситуация имеет место, только если B.NAME является уникальным индексом, и вы пытаетесь вставить те же значения. Первая вставка ожидает освобождения индекса A.ID, что никогда не произойдет из-за дублирования значения B.NAME.

0 голосов
/ 06 апреля 2010

Основано на функции из высокопроизводительного блога mysql.

Мне удалось реализовать следующий код обработки взаимоблокировок в PHP:

/* maximum number of attempts for deadlock */
$MAX_ATTEMPS = 20;

 /* query */
 $sql = "INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world')"; 

 /* current attempt counter */
 $current = 0;

 /* try to query */
 while ($current++ <$MAX_ATTEMPS) 
 {
     $result = mysql_query($sql);
     if(!$result && ( mysql_errno== '1205' || mysql_errno == '1213'  ) )
         continue;
     else
         break;
 }  
 }

Надеюсь, это даст вам хорошие идеи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...