Какие существуют аномалии конкурентного доступа при работе с транзакциями?

Ответ

При одновременном выполнении транзакций без должной изоляции могут возникать следующие аномалии:

  1. Потерянное обновление (Lost Update)

    • Проблема: Одна транзакция перезаписывает изменения другой.
    • Сценарий:
      1. Транзакция 1 читает баланс счёта (1000).
      2. Транзакция 2 читает тот же баланс (1000).
      3. Транзакция 1 добавляет 100 и записывает баланс (1100).
      4. Транзакция 2 добавляет 200 к своему прочитанному значению и записывает баланс (1200).
    • Результат: Обновление от Транзакции 1 потеряно. Итоговый баланс 1200, а не 1300.
  2. Грязное чтение (Dirty Read)

    • Проблема: Транзакция читает данные, которые были изменены другой транзакцией, но ещё не зафиксированы (не COMMIT). Если вторая транзакция откатится (ROLLBACK), первая будет работать с недействительными данными.
    • Предотвращается уровнем: Read Committed и выше.
  3. Неповторяемое чтение (Non-Repeatable Read)

    • Проблема: Повторное чтение одной и той же строки в рамках одной транзакции возвращает разные значения. Это происходит, если другая транзакция успевает изменить и зафиксировать эти данные между чтениями.
    • Предотвращается уровнем: Repeatable Read и выше.
  4. Фантомное чтение (Phantom Read)

    • Проблема: Повторное выполнение запроса с условием выборки (например, SELECT ... WHERE age > 30) в рамках одной транзакции возвращает разное количество строк. Это происходит, если другая транзакция добавляет или удаляет строки, удовлетворяющие этому условию.
    • Предотвращается уровнем: Serializable.

Правильный выбор уровня изоляции транзакций является основным способом предотвращения этих аномалий на уровне СУБД.

Ответ 18+ 🔞

Давай разжую эту тему про транзакции, а то звучит страшно, а на деле — обычные базарные разборки в базе данных. Представь, что данные в таблице — это общая хата, а транзакции — это мы с тобой, которые пытаемся одновременно там прибраться, переставить мебель и спиздить из холодильника последнюю банку огурцов. Без правил начинается пиздец, который умные дядьки назвали «аномалиями».

1. Потерянное обновление (Lost Update)

Суть: Один мудак затирает результаты работы другого. Классика, как в очереди за пивом.

  • Сценарий:
    1. Ты смотришь на счёт: «О, 1000 рублей. Накину сотню» (читаешь 1000).
    2. Я, не глядя, тоже смотрю этот же счёт: «О, 1000 рублей. Накину двести» (читаю те же 1000).
    3. Ты быстренько прибавляешь свою сотню и пишешь: «Всё, теперь тут 1100».
    4. А я, тупой, прибавляю свои 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) — это как настройка степени паранойи и контроля в этой общей хате-базе. Выбираешь уровень — выбираешь, от каких именно ебак в своей работе ты готов защититься, ценой производительности. А не выбираешь — получаешь все эти аномалии разом, как в помойке.