Что такое deadlock (взаимная блокировка) в SQL?

Ответ

Deadlock (взаимная блокировка) — это ситуация в многопользовательской СУБД, когда две или более транзакции бесконечно ожидают друг друга, потому что каждая удерживает блокировку на ресурсе, который требуется другой.

Классический пример:

-- ТРАНЗАКЦИЯ A
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- Блокирует запись id=1
-- Транзакция A ждет блокировки на id=2...

-- ТРАНЗАКЦИЯ B (выполняется параллельно)
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 50 WHERE id = 2; -- Блокирует запись id=2
-- Транзакция B ждет блокировки на id=1...

-- Теперь A ждет B, а B ждет A. Deadlock!
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- Транзакция A (ждет)
UPDATE accounts SET balance = balance + 50 WHERE id = 1;  -- Транзакция B (ждет)

Как СУБД решает проблему? Большинство СУБД (SQL Server, PostgreSQL, Oracle) имеют детектор взаимных блокировок, который периодически проверяет систему на наличие циклических зависимостей. При обнаружении deadlock:

  1. СУБД выбирает одну транзакцию в качестве "жертвы" (обычно ту, которую дешевле откатить).
  2. Завершает её с ошибкой (например, ERROR 1213 (40001): Deadlock found в MySQL).
  3. Все изменения "жертвы" откатываются, и её блокировки снимаются.
  4. Другие транзакции в deadlock могут продолжить выполнение.

Как предотвращать deadlock на практике?

  1. Унифицированный порядок доступа: Всегда блокируйте ресурсы (например, строки таблиц) в одном и том же порядке во всех транзакциях. В примере выше, если бы обе транзакции сначала обновляли id=1, а потом id=2, deadlock бы не возник.
  2. Короткие транзакции: Выполняйте как можно меньше операций в одной транзакции и завершайте её быстро.
  3. Использование таймаутов: Установите SET LOCK_TIMEOUT 5000; (5 секунд). Если транзакция не может получить блокировку за это время, она прервется, не дожидаясь deadlock.
  4. Изоляция READ COMMITTED SNAPSHOT (в SQL Server) или REPEATABLE READ (в некоторых СУБД): Эти уровни изоляции могут уменьшить количество блокировок, используя управление версиями строк.
  5. Тщательный дизайн индексов: Отсутствие подходящего индекса может привести к блокировке всей таблицы вместо одной строки, увеличивая вероятность deadlock.

Ответ 18+ 🔞

Давай разжую про эти ваши дедлоки, а то у меня от этой темы уже волнение ебать какое-то. Представь себе, сидят два чувака в базе данных, оба в транзакциях, и оба ждут друг от друга хуй знает чего. Это и есть deadlock, или, по-нашему, взаимная блокировка. Ситуация, блядь, классическая: каждый держит в заложниках то, что нужно другому, и оба уперлись, как бараны.

Вот смотри, живой пример, чтобы ты сам от себя охуел от наглядности:

-- ЧУВАК А
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- Он прихватил запись с id=1
-- И теперь сидит, бздит, ждёт, когда ему отдадут id=2...

-- ЧУВАК Б (делает всё параллельно, умник)
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 50 WHERE id = 2; -- А этот, хитрая жопа, уже схватил id=2
-- И тоже уставился в потолок, мечтая о id=1...

-- И всё, приехали. А ждёт Б, Б ждёт А. Ёперный театр, полный тупик!
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- Чувак А (висит в ожидании)
UPDATE accounts SET balance = balance + 50 WHERE id = 1;  -- Чувак Б (тоже повис)

И что, теперь они так до скончания веков будут? Да хуй там! Умные дядьки, которые эти СУБД писали, не лыком шиты. У них там есть специальный стукач, детектор дедлоков. Он, сука, как надзиратель, ходит и смотрит, не образовалась ли где порочная петля из транзакций. Как находит — сразу начинается разборка.

  1. Он выбирает одного пацана на роль "козла отпущения" — обычно того, кого проще и дешевле откатить.
  2. Бах — и выкидывает ему ошибку, типа "Deadlock found, иди на хуй".
  3. Всё, что этот бедолага натворил, откатывается к хуям, и все его блокировки снимаются.
  4. Второй чувак, довольный такой, получает наконец то, что хотел, и спокойно продолжает работу. Жестоко, но справедливо.

Как не попасть в такую жепень? Слушай сюда, э бошка думай, запоминай правила выживания:

  1. Порядок — всё! Всегда лезь к данным в одном и том же порядке. В нашем примере, если бы оба чувака сначала хватали id=1, а потом уже id=2, никакой бы драки не было. Один бы взял, сделал дело, отпустил, потом второй. А так — мудя получилась.
  2. Не размазывай сопли. Делай транзакции короткими и яростными. Залетел, быстро сделал что нужно, и вышел. Не надо там чаи гонять и полбазы блокировать.
  3. Ставь таймер. Можно сказать системе: "Слушай, если я больше 5 секунд жду — бросай это дело". Команда типа SET LOCK_TIMEOUT 5000;. Лучше уйти с позором, чем висеть вечность.
  4. Играй настройками. Некоторые уровни изоляции (типа READ COMMITTED SNAPSHOT) меньше блокируют друг друга, используя хитрые версии данных. Но тут, чувак, надо понимать, что делаешь, а то будет тебе хиросима.
  5. Индексы, блядь, расставь! Это самое важное. Если нет нормального индекса, то чтобы одну строку обновить, система может заблокировать всю таблицу целиком. А когда ты всю таблицу лочишь, вероятность, что кто-то врежется в тебя и получится дедлок — овердохуища. Так что следи за хозяйством, не будь распиздяем.