Блокировки: SHARE, UPDATE, FOR UPDATE
Что такое блокировки и зачем они нужны? 🔒
Блокировки в SQL — это механизм контроля доступа к данным при одновременной работе нескольких транзакций. Они предотвращают конфликты при чтении и изменении одних и тех же строк разными пользователями.
Представьте ситуацию: два клиента бронируют один и тот же билет на концерт. Без блокировок может возникнуть двойное бронирование — и это лишь один из примеров проблем, которые решают блокировки.
Типы блокировок в SQL
В PostgreSQL и других СУБД есть несколько ключевых типов блокировок строк. Разберём три самых важных:
1. SHARE — блокировка для чтения 📖
Эта блокировка ставится, когда транзакция читает данные и хочет гарантировать, что они не изменятся до её завершения.
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR SHARE;
-- Другие транзакции могут тоже ставить SHARE, но не UPDATE/FOR UPDATE
COMMIT;
🔹 Когда использовать?
- Когда нужно прочитать данные и быть уверенным, что они не изменятся до конца транзакции.
- Например, проверка баланса перед списанием средств.
2. UPDATE — блокировка для изменения ✏️
Блокировка UPDATE автоматически ставится при изменении строки (через UPDATE или DELETE). Она предотвращает одновременное изменение одной записи разными транзакциями.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Если другая транзакция попытается изменить эту же строку, она будет ждать
COMMIT;
🔹 Особенности:
- Совместима с SHARE, но не с другими UPDATE или FOR UPDATE.
- Автоматически устанавливается при UPDATE/DELETE.
3. FOR UPDATE — эксклюзивная блокировка ⚡
Самая строгая блокировка. Гарантирует, что другие транзакции не смогут ни читать (с FOR SHARE), ни изменять заблокированные строки.
BEGIN;
SELECT * FROM orders WHERE user_id = 5 FOR UPDATE;
-- Другие транзакции не смогут даже прочитать эти строки с FOR SHARE
COMMIT;
🔹 Когда использовать?
- Когда нужно получить монопольный доступ к данным (например, при бронировании билетов).
- Для предотвращения состояния гонки (race condition).
Практический пример: банковский перевод 💰
Допустим, мы делаем перевод денег между счетами. Без блокировок возможны ошибки:
-- ❌ Опасный код (без блокировок)
BEGIN;
-- Чтение без блокировки (может измениться до UPDATE!)
SELECT balance FROM accounts WHERE id = 1;
SELECT balance FROM accounts WHERE id = 2;
-- ...логика проверки...
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
А вот правильный вариант с блокировками:
-- ✅ Безопасный перевод
BEGIN;
-- Блокируем обе строки перед изменением
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
SELECT * FROM accounts WHERE id = 2 FOR UPDATE;
-- Теперь балансы гарантированно не изменятся
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
Совместимость блокировок 🧩
| Запрос | SHARE | UPDATE | FOR UPDATE |
|---|---|---|---|
| SHARE | ✅ | ✅ | ❌ |
| UPDATE | ✅ | ❌ | ❌ |
| FOR UPDATE | ❌ | ❌ | ❌ |
🔹 Как читать таблицу:
- Если текущая транзакция держит блокировку в строке, другие транзакции могут поставить только совместимые (✅).
- FOR UPDATE — самая строгая: она несовместима ни с чем.
Главные выводы 🎯
SHARE— для безопасного чтения (другие могут читать, но не изменять).UPDATE— автоматически ставится при изменении строки.FOR UPDATE— эксклюзивный доступ (никто другой не может ни читать, ни изменять).- Всегда блокируйте данные перед изменением, особенно в финансовых операциях!
Попробуйте применить эти блокировки в своих транзакциях — и ваши данные всегда будут в безопасности! 🔐