Ответ
При одновременном выполнении транзакций без должной изоляции могут возникать следующие аномалии:
-
Потерянное обновление (Lost Update)
- Проблема: Одна транзакция перезаписывает изменения другой.
- Сценарий:
- Транзакция 1 читает баланс счёта (1000).
- Транзакция 2 читает тот же баланс (1000).
- Транзакция 1 добавляет 100 и записывает баланс (1100).
- Транзакция 2 добавляет 200 к своему прочитанному значению и записывает баланс (1200).
- Результат: Обновление от Транзакции 1 потеряно. Итоговый баланс 1200, а не 1300.
-
Грязное чтение (Dirty Read)
- Проблема: Транзакция читает данные, которые были изменены другой транзакцией, но ещё не зафиксированы (не
COMMIT). Если вторая транзакция откатится (ROLLBACK), первая будет работать с недействительными данными. - Предотвращается уровнем:
Read Committedи выше.
- Проблема: Транзакция читает данные, которые были изменены другой транзакцией, но ещё не зафиксированы (не
-
Неповторяемое чтение (Non-Repeatable Read)
- Проблема: Повторное чтение одной и той же строки в рамках одной транзакции возвращает разные значения. Это происходит, если другая транзакция успевает изменить и зафиксировать эти данные между чтениями.
- Предотвращается уровнем:
Repeatable Readи выше.
-
Фантомное чтение (Phantom Read)
- Проблема: Повторное выполнение запроса с условием выборки (например,
SELECT ... WHERE age > 30) в рамках одной транзакции возвращает разное количество строк. Это происходит, если другая транзакция добавляет или удаляет строки, удовлетворяющие этому условию. - Предотвращается уровнем:
Serializable.
- Проблема: Повторное выполнение запроса с условием выборки (например,
Правильный выбор уровня изоляции транзакций является основным способом предотвращения этих аномалий на уровне СУБД.
Ответ 18+ 🔞
Давай разжую эту тему про транзакции, а то звучит страшно, а на деле — обычные базарные разборки в базе данных. Представь, что данные в таблице — это общая хата, а транзакции — это мы с тобой, которые пытаемся одновременно там прибраться, переставить мебель и спиздить из холодильника последнюю банку огурцов. Без правил начинается пиздец, который умные дядьки назвали «аномалиями».
1. Потерянное обновление (Lost Update)
Суть: Один мудак затирает результаты работы другого. Классика, как в очереди за пивом.
- Сценарий:
- Ты смотришь на счёт: «О, 1000 рублей. Накину сотню» (читаешь 1000).
- Я, не глядя, тоже смотрю этот же счёт: «О, 1000 рублей. Накину двести» (читаю те же 1000).
- Ты быстренько прибавляешь свою сотню и пишешь: «Всё, теперь тут 1100».
- А я, тупой, прибавляю свои 200 к своей старой копии (1000) и с чувством выполненного долга пишу: «Всё, теперь тут 1200».
- Итог: Твои 100 рублей просто испарились, нахуй. Вместо 1300 получилось 1200. Твоё обновление похерили. Пиздец обидно.
2. Грязное чтение (Dirty Read)
Суть: Читаешь хуйню, которую кто-то накалякал, но ещё не решил — оставить это или смыть в унитаз.
- Как: Я начинаю транзакцию, пишу в поле «статус_заказа» = «ОПЛАЧЕНО, ЫЫЫ». Ты, не дожидаясь, пока я скажу
COMMIT(то есть «всё, ок, оставляю»), читаешь этот статус и радостно начинаешь собирать заказ. А я тут понимаю, что деньги не пришли, и делаюROLLBACK(откат). Статус возвращается к «НЕ ОПЛАЧЕНО». А ты уже полкоробки собрал, лох. Работал с ебучой грязной данностью. - Кто спасает: Уровень изоляции
Read Committedи выше. Он тебе не даст читать незакоммиченную хуету.
3. Неповторяемое чтение (Non-Repeatable Read)
Суть: Прочитал одно, через секунду перечитал — а там уже другое. Как будто тебе пиздят прямо из-под носа.
- Как: Ты в транзакции проверяешь баланс Васи: «Так, 500 рублей». Решаешь дать ему кредит. Ищешь документы пять секунд. Снова проверяешь баланс, чтобы пересчитать — а там уже 50 рублей, блядь! Потому что пока ты копался, другая транзакция списала с Васи 450 и закоммитилась. И теперь твои расчёты — говно. В рамках одной своей транзакции ты не можешь дважды прочитать одно и то же. Пиздец, да?
- Кто спасает: Уровень
Repeatable Read. Он как бы «замораживает» для тебя снимок данных на время твоей транзакции.
4. Фантомное чтение (Phantom Read)
Суть: Самый жуковый призрак. Ты считаешь строки, а они, сука, размножаются или исчезают.
- Как: Ты делаешь выборку:
SELECT * FROM users WHERE age > 30. Тебе вернулось 10 строк. Ты думаешь: «Окей, десять старичков». Пока ты что-то с ними делаешь, другая транзакция успевает добавить двух новых развалюх (age=35, age=40) и закоммититься. Ты, для верности, повторяешь точно такой же запросSELECT * FROM users WHERE age > 30. БАМ! Тебе уже 12 строк. Откуда взялись две новые? Фантомы, ёпта! Как будто их не было, а теперь есть. Или наоборот — были и испарились. - Кто спасает: Только тотальный уровень
Serializable. Он расставляет всё по полочкам так, будто транзакции выполняются строго одна за другой, без всякого параллелизма. Медленно, зато без сюрпризов.
Вывод, блядь: Вся эта песня с уровнями изоляции (Read Uncommitted, Read Committed, Repeatable Read, Serializable) — это как настройка степени паранойи и контроля в этой общей хате-базе. Выбираешь уровень — выбираешь, от каких именно ебак в своей работе ты готов защититься, ценой производительности. А не выбираешь — получаешь все эти аномалии разом, как в помойке.