TGViewer
EvApps EvApps @evapps_team · 199 subscribers
Post #1532 70
🔒 BEGIN/COMMIT не спасёт так, как ты думаешь
Обернул код в транзакцию — и кажется, что теперь всё под защитой, гонки не страшны, данные консистентны
На деле транзакция даёт меньше, чем от неё ждут, а самое интересное прячется в уровнях изоляции, которые почти никто не трогает
Разберём, что реально гарантирует транзакция и где она молча тебя подведёт

📦 Что даёт транзакция на самом деле

Главное, что делает BEGIN ... COMMIT — это атомарность: либо применились все изменения, либо ни одного
Списал с одного счёта, зачислил на другой, между ними упало — откатится всё, половина денег нигде не зависнет
Это работает и это ценно
Но есть второй вопрос, про который забывают: а что транзакция видит, пока рядом крутятся другие?
Вот это и есть изоляция — и у неё несколько уровней, от слабого к строгому
По умолчанию стоит не самый строгий

🎚 Уровни изоляции по-простому Read Committed
Ты видишь только то, что другие уже закоммитили
Звучит норм, но засада: два одинаковых SELECT внутри одной твоей транзакции могут вернуть разное — если между ними кто-то успел закоммитить изменение
То есть данные под тобой могут поменяться прямо по ходу

Repeatable Read
Транзакция работает как будто со снимком данных на момент своего старта
Сколько раз ни спроси — видишь одно и то же, даже если снаружи уже всё поменяли
В MySQL/InnoDB, кстати, это дефолт, а не Read Committed — приятная разница, о которую спотыкаются при переезде между базами

Serializable
Самый строгий: база ведёт себя так, будто транзакции шли строго по очереди, одна за другой
Максимальная защита, но за неё платишь — если база видит, что две транзакции конфликтуют, она откатывает одну с ошибкой сериализации, и ты должен её поймать и повторить

💥 Где это стреляет
Списание с баланса из поста про гонки:
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- прочитал 100
-- посчитал в коде: 100 - 50 = 50
UPDATE accounts SET balance = 50 WHERE id = 1;
COMMIT;
Многие думают: «я же в транзакции, всё ок»
А вот и нет
На дефолтном Read Committed две такие транзакции спокойно выполнятся параллельно, обе прочитают 100, обе запишут 50 — одно списание потерялось
Транзакция тут не помогла, потому что проблема не в атомарности, а в изоляции

🛠 Что с этим делать
Три рабочих пути, по ситуации:
Считать атомарно в самой базе, не таща значение в код:
UPDATE accounts SET balance = balance - 50
WHERE id = 1 AND balance >= 50;
Заблокировать строку на время работы через SELECT ... FOR UPDATE — тогда вторая транзакция подождёт, пока не закоммитишь
Поднять уровень до Serializable — база сама поймает конфликт, но тогда готовь ретрай на ошибку сериализации

⏱️ И держи транзакции короткими
Пока транзакция открыта, она держит блокировки и мешает другим
Открыл транзакцию, а внутри пошёл в чужой API или задумался на пользовательском вводе — и вот уже полбазы стоит в очереди за твоими строками
В транзакции должно быть только то, что реально должно примениться разом, и ничего медленного

А вы уровень изоляции вообще трогали — или живёте на дефолте и не паритесь? 🤔

#database #postgres #backend #sql #dev #programming
  • 🔥 1
More from @evapps_team
  1. Sep 21, 2026🧩 Что тут не так? Код-загадка Формат новый - показываю код, ты угадываешь подвох, ниже ра…
  2. Sep 18, 2026🎭 Мифы про производительность, в которые верят даже опытные Миф 1: "Меньше строк кода - б…
  3. Sep 16, 2026🚨 Как перевод денег уронил нам прод ⏰ 19:10 Задеплоили долгожданное - переводы между коше…
  4. Sep 14, 2026🗃 Кэш поставили, а он отдаёт старьё В программировании две сложные вещи - инвалидация кэш…
  5. Sep 11, 2026🔌 "Too many connections" - и почему база падает под нагрузкой Под нагрузкой прилетает FAT…
  6. Sep 11, 2026Пока вы наслаждаетесь пятницей, мы напоминаем, что уже завтра стартует одна из наших любим…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →