Как предотвратить взаимоблокировку базы данных в параллельных транзакциях?

Сценарий:

Транзакция A начинается...

START TRANSACTION;
UPDATE table_name SET column_name=column_name+1 WHERE id = 1 LIMIT 1;

В то же время начинается транзакция B...

START TRANSACTION;
UPDATE table_name SET column_name=column_name+1 WHERE id = 2 LIMIT 1;
UPDATE table_name SET column_name=column_name-1 WHERE id = 1 LIMIT 1;
COMMIT;

Сейчас транзакция B ожидает строки 1, которая заблокирована в транзакции A.

И транзакция A продолжается...

UPDATE table_name SET column_name=column_name-1 WHERE id = 2 LIMIT 1;
COMMIT;

И теперь у нас есть мертвая блокировка, поэтому обе транзакции ждут друг друга, чтобы разблокировать строку, которую они хотят обновить :'(

Как я спросил в заголовке, как мы можем предотвратить взаимоблокировки в транзакциях РСУБД?

Я думаю, что единственный способ исправить эту ситуацию — откатить транзакцию B и выполнить ее заново. Но как нам узнать, что мы в тупике, и сразу выйти из него, и как мы можем гарантировать, что не попадем в запас и бесконечный цикл (на очень тяжелых веб-приложениях, например).

Если необходимо, я использую MySQL. Но любое решение для других СУБД приветствуется - за помощь другим людям, пришедшим сюда из Google :)


person user5483434    schedule 21.02.2016    source источник
comment
Вы не можете предотвратить взаимоблокировки, однако вы можете написать код, который минимизирует их возникновение.   -  person Shadow    schedule 21.02.2016
comment
@trincot Я думаю, что да, транзакция A блокирует строку № 1, транзакция B блокирует строку № 2 и ждет, пока транзакция A разблокирует строку № 1, а транзакция A ждет, пока транзакция B разблокирует строку № 2, поэтому обе транзакции ждут друг друга. Можете ли вы поправить меня, если я ошибаюсь? Спасибо.   -  person user5483434    schedule 21.02.2016
comment
@Shadow Не могли бы вы сообщить мне, как я могу свести к минимуму их появление?   -  person user5483434    schedule 21.02.2016
comment
Вы могли бы написать книгу об этом. Ваш вопрос слишком широк, в том числе так много rdbms тоже не помогают. Я голосую за закрытие этого вопроса.   -  person Shadow    schedule 21.02.2016
comment
Конкретный вопрос MySQL. Что такое [тип таблицы] (stackoverflow.com/questions/15678406/), в зависимости от того, какой это тип, вы можете использовать соответствующие уровни изоляции транзакций для одновременного запуска двух транзакций. Я предполагаю, что ваш тип таблицы - ISAM, и есть блокировка на уровне таблицы, и транзакция B ожидает завершения транзакции A, чтобы B мог начать выполнение.   -  person SagaciousLearner    schedule 21.02.2016
comment
@SagaciousLearner Чтобы предотвратить блокировку всей таблицы при вставках и обновлениях, мы используем InnoDB. На самом деле все таблицы InnoDB.   -  person user5483434    schedule 21.02.2016
comment
В этом случае просмотрите Уровни изоляции, используя соответствующие уровень изоляции должен помочь предотвратить блокировку на уровне строк. Рекомендовать SERIALIZABLE уровень изоляции msdn объясняет уровень изоляции намного лучше.   -  person SagaciousLearner    schedule 21.02.2016


Ответы (1)


Большинство баз данных (если не все) автоматически обнаруживают тупик, выбирают один сеанс в качестве жертвы и автоматически откатывают транзакцию этого сеанса, чтобы выйти из тупика. Например, вот обнаружение взаимоблокировок MySQL и откат документация.

Взаимоблокировки — это ошибки программирования. Одним из простых способов избежать взаимоблокировок является гарантия того, что вы всегда блокируете строки в определенном порядке. Например, если у вас есть транзакция, которая хочет обновить две разные строки, всегда обновляйте строку с меньшей id первой и большей id второй. Если ваш код всегда делает это, у вас, по крайней мере, не будет взаимоблокировок на уровне строк. Кроме того, внедрите соответствующую сериализацию для критических разделов кода. Что именно это влечет за собой, очень зависит от вашего приложения.

person Justin Cave    schedule 21.02.2016
comment
Спасибо, Джастин. На самом деле я слышал тупиковую блокировку в этот вопрос (который я задал несколько часов назад). Можете ли вы помочь мне с взаимоблокировками на уровне строк? Есть ли другие взаимоблокировки в InnoDB? (уровень таблицы? любой другой?) Кроме того, английский не является моим родным языком, не могли бы вы объяснить, что вы имеете в виду при реализации соответствующей сериализации для критических разделов в вашем коде, пожалуйста? Сейчас я прочитаю связанный URL. Спасибо еще раз. - person user5483434; 21.02.2016
comment
@ user5483434 — Подавляющее большинство взаимоблокировок — это взаимоблокировки на уровне строк, но есть множество других ресурсов, которые вы можете заблокировать, что может привести к взаимоблокировке. Если вы ищете тупик параллельного программирования, есть (много) книг по параллельному программированию и подходам к реализации соответствующей сериализации. Это не то, что реально можно обсудить на такого рода форуме. И, как я уже сказал, то, что именно это влечет за собой, сильно зависит от вашего приложения. - person Justin Cave; 21.02.2016