Что такое «грязное чтение» (Dirty Read) в контексте транзакций СУБД?

Ответ

Грязное чтение (Dirty Read) — это аномалия изоляции транзакций, при которой одна транзакция читает неподтверждённые (незакоммиченные) изменения другой, параллельной транзакции. Если первая транзакция откатывается, вторая прочитает данные, которые никогда официально не существовали в базе, что приводит к несогласованности.

Пример на SQL:

-- Транзакция 1 (T1)                    -- Транзакция 2 (T2)
BEGIN TRANSACTION;
UPDATE accounts SET balance = 1500      
WHERE id = 1; -- Баланс стал 1500
                                        BEGIN TRANSACTION;
                                        SELECT balance FROM accounts
                                        WHERE id = 1; -- T2 читает 1500 (Грязное чтение!)
ROLLBACK; -- T1 откатывается, баланс возвращается к 1000
                                        COMMIT; -- T2 работает с неверным значением 1500

Уровни изоляции, предотвращающие грязное чтение:

  • READ COMMITTED (и выше, такие как REPEATABLE READ, SERIALIZABLE) в стандарте SQL.
  • В Java Persistence API (JPA) для этого используется @Transactional(isolation = Isolation.READ_COMMITTED).

Почему это проблема:

  • Нарушает атомарность и согласованность данных.
  • Может привести к принятию бизнес-решений на основе несуществующих данных.

Как избежать: Используйте уровень изоляции транзакций не ниже READ COMMITTED, который гарантирует, что транзакция видит только зафиксированные изменения других транзакций.

Ответ 18+ 🔞

Да ты посмотри, что творят-то, а? Вот сидит себе транзакция первая, Т1, значит, и думает: «Ща я этому счёту баланс на 1500 обновлю, пусть порадуется». Обновила, сука, и довольная такая.

А тут, из-за угла, как выскакивает вторая, Т2, хитрая жопа! Глянула на этот самый баланс — о, 1500! — и уже строит планы, как эти виртуальные деньги потратить. «Овердохуища, какой клиент щедрый!» — думает.

И тут БАЦ! Первая транзакция, Т1, такая: «Ой, блядь, я передумала!» И — хрясь! — ROLLBACK. Баланс-то обратно на 1000 откатился, как ни в чём не бывало.

А вторая, Т2, уже с этими фантомными 1500 в голове коммитится и идёт дальше. И что мы имеем? А имеем мы, блядь, грязное чтение (Dirty Read)! Одна начиталась говна, которого в итоге и не было, и теперь вся бизнес-логика поехала, как у мартышлюшки крыша. Приняла решение на основе данных, которые хуй с горы свалились и тут же испарились. Пиздец, да?

Вот тебе наглядный пиздец в коде:

-- Транзакция 1 (T1)                    -- Транзакция 2 (T2)
BEGIN TRANSACTION;
UPDATE accounts SET balance = 1500      
WHERE id = 1; -- Баланс стал 1500
                                        BEGIN TRANSACTION;
                                        SELECT balance FROM accounts
                                        WHERE id = 1; -- T2 читает 1500 (Грязное чтение!)
ROLLBACK; -- T1 откатывается, баланс возвращается к 1000
                                        COMMIT; -- T2 работает с неверным значением 1500

Вот так вот, в рот меня чих-пых! Т2 прочитала незакоммиченный пиздёж от Т1, а потом этот пиздёж взял и рассосался. И сидит Т2 теперь с чувством, будто её ебали в сраку и не кончили.

Как от этого спастись, чтобы не было мучительно больно? Да хуй сложного! Надо просто не читать чужие черновики, пока они не стали официальной бумажкой. В SQL для этого есть уровень изоляции READ COMMITTED (или любой повыше, REPEATABLE READ, SERIALIZABLE — там вообще жесть, но надёжно). Он как батя строгий: «Не трогай, пока брат свою игрушку до конца не собрал!».

В Java (JPA) это выглядит так, будто ты ставишь блокпост:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void someSensitiveMethod() {
    // Тут можно читать, не боясь говна из параллельных транзакций
}

Короче, мораль: если не хочешь, чтобы твоё приложение жило в мире фантомов и галлюцинаций, выставляй изоляцию на READ_COMMITTED как минимум. А то будет как в том анекдоте: «Деньги есть? — Вроде были...».