как избежать тупика в MySQL - PullRequest
10 голосов
/ 18 марта 2010

У меня следующий запрос (все таблицы innoDB)

INSERT INTO busy_machines(machine) 
               SELECT machine FROM all_machines 
               WHERE machine NOT IN (SELECT machine FROM busy_machines) 
               and machine_name!='Main' 
               LIMIT 1

Что вызывает тупик, когда я запускаю его в потоках, очевидно, из-за внутреннего выбора, верно?

Я получаю ошибку:

(1213, 'Deadlock found when trying to get lock; try restarting transaction')

Как мне избежать тупика? Есть ли способ изменить запрос, чтобы он работал, или мне нужно сделать что-то еще?

Ошибка не возникает всегда, конечно, только после многократного выполнения этого запроса и в нескольких потоках.

Ответы [ 2 ]

12 голосов
/ 18 марта 2010

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

Каждый раз, когда вы вставляете / обновляете / удаляете строку, блокировка получается. Чтобы избежать взаимоблокировки, необходимо убедиться, что параллельные транзакции не обновляют строки в порядке, который может привести к взаимоблокировке. Вообще говоря, чтобы избежать взаимоблокировки , вы должны получать блокировку всегда в одном и том же порядке даже в другой транзакции (например, всегда сначала таблица A, затем таблица B).

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

Однако может возникнуть тупик, если отсутствуют индексы . Когда строка вставляется / обновляется / удаляется, базе данных необходимо проверить реляционные ограничения, то есть убедиться, что отношения согласованы. Для этого базе данных необходимо проверить внешние ключи в связанных таблицах. Это может привести к получению другой блокировки, кроме строки, которая была изменена. Тогда убедитесь, что у индекса всегда есть внешние ключи (и, конечно, первичные ключи), иначе это может привести к блокировке таблицы вместо блокировке строки . Если блокировка таблицы происходит, конкуренция за блокировку выше и вероятность взаимоблокировки увеличивается.

Не уверен, что именно происходит в вашем случае, но, возможно, это поможет.

5 голосов
/ 18 марта 2010

Вероятно, вы получите лучшую производительность, если замените «NOT IN» на внешнее соединение.

Вы также можете разделить это на два запроса, чтобы избежать вставки и выбора одной и той же таблицы в одном запросе.

Примерно так:

           SELECT a.machine 
           into @machine
           FROM all_machines a
           LEFT OUTER JOIN busy_machines b on b.machine = a.machine
           WHERE a.machine_name!='Main' 
           and b.machine IS NULL 
           LIMIT 1;

           INSERT INTO busy_machines(machine) 
           VALUES (@machine);
...