Что такое блокировки в транзакциях баз данных?

Ответ

Блокировки — это механизм СУБД, который предотвращает конфликтующие операции (одновременную запись в одни данные) параллельными транзакциями, обеспечивая согласованность. Основные типы в InnoDB (MySQL):

  • Разделяемая блокировка (Shared Lock, S): Разрешает другим транзакциям читать данные, но запрещает устанавливать эксклюзивные блокировки на них. Используется для SELECT ... LOCK IN SHARE MODE.
  • Эксклюзивная блокировка (Exclusive Lock, X): Запрещает другим транзакциям как читать (с блокировкой), так и изменять данные. Используется для операций UPDATE, DELETE и SELECT ... FOR UPDATE.

Пример проблемы, которую решают блокировки (состояние гонки):

  1. Транзакция A читает баланс (100$).
  2. Транзакция B тоже читает баланс (100$).
  3. A и B одновременно рассчитывают новый баланс (+10$).
  4. A записывает 110$.
  5. B записывает 110$ (потеряно изменение A).

Решение с SELECT ... FOR UPDATE:

-- Транзакция 1
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE; -- X-блокировка на строку id=1
-- ... вычисления ...
UPDATE accounts SET balance = 110 WHERE id = 1;
COMMIT;

-- Транзакция 2 будет ждать разблокировки строки id=1, прежде чем выполнить свой SELECT FOR UPDATE.

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

Ответ 18+ 🔞

Слушай, а вот про эти ваши блокировки в базе — это вообще отдельная песня, ёпта. Представь себе, что база данных — это такой общий холодильник в общаге. Блокировки — это чтобы два алкаша одновременно не полезли за последней банкой пива, а то один возьмёт, второй тоже потянется, и оба нихуя не получат, только бутылку разобьют. Вот для этого и придумали систему: "Я первый, стой, не трогай, пока я не закончил".

Так вот, в этой вашей InnoDB, если по-простому, есть два основных типа "предупреждений":

  • Разделяемая блокировка (Shared Lock, S): Это как крикнуть на всю кухню: "Эй, народ, я тут смотрю, что в холодильнике лежит! Можете тоже посмотреть, но НЕ СМЕЙТЕ ЭТО ЖРАТЬ, пока я не отойду!" Используется для SELECT ... LOCK IN SHARE MODE. Все могут читать, но менять — ни-ни.
  • Эксклюзивная блокировка (Exclusive Lock, X): А это уже серьёзнее. Ты подходишь к холодильнику, хватаешь банку, и орёшь: "ВСЕ ОТОЙДИТЕ ОТ ХОЛОДИЛЬНИКА! ЭТО МОЁ, Я ЭТО ЕМ ИЛИ ВЫЛИВАЮ В УНИТАЗ, ВАМ ДЕЛАТЬ НЕЧЕГО!" Это для UPDATE, DELETE и SELECT ... FOR UPDATE. Ни читать с пристрастием, ни, тем более, писать нельзя.

Пример проблемы, которую решают блокировки (состояние гонки): Вот смотри, классика жанра, просто пиздец:

  1. Транзакция А смотрит на счёт: "О, 100 баксов, красота".
  2. Транзакция Б, не глядя по сторонам, тоже тычется в те же данные: "Ага, тоже вижу 100 баксов".
  3. И А, и Б одновременно думают: "Прибавим 10, будет 110, всё гуд".
  4. А быстренько пишет: "Баланс = 110".
  5. Б, тупая манда с ушами, пишет поверх: "Баланс = 110". И где прибавление от транзакции А? Правильно, накрылось медным тазом. Потерялось, блядь. И вместо 120 на счету — нихуя, 110.

Решение с SELECT ... FOR UPDATE: Вот чтобы такого пиздеца не было, умные дяди придумали волшебную команду. Она как крик "Отойди, щас стрелять буду!":

-- Транзакция 1
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE; -- Тут мы вешаем на строку с id=1 здоровенный амбарный замок (X-блокировку)
-- ... сидим, вычисляем, чай пьём ...
UPDATE accounts SET balance = 110 WHERE id = 1;
COMMIT; -- Только тут замок снимается

-- Транзакция 2 в это время подойдёт, попробует сделать свой SELECT FOR UPDATE на ту же строку и... обломись. Будет стоять и ждать, как дурак, пока первая транзакция не снимет свой замок. Волнение ебать, а терпения — ноль.

Важные последствия, о которых орут все админы: Если ты начнёшь вешать эти эксклюзивные замки на всё подряд, да ещё и надолго — жди беды. Может случиться взаимоблокировка (deadlock). Это когда твоя транзакция ждёт разблокировки от второй, а вторая — от первой. И стоят они, смотрят друг на друга, как два барана. База в итоге одну из них прибьёт, но осадочек-то останется, да и производительность накроется. Поэтому золотое правило: захватывай блокировки в одном и том же порядке (чтобы не было разночтений, кто кого ждёт) и не растягивай транзакции, будто резиновые. Коротко и ясно — залог здоровья твоей базы.