Что такое ‘грязное чтение’ (Dirty Read) в базах данных?

Ответ

Грязное чтение (Dirty Read) — это проблема, возникающая в многопоточных системах при работе с базами данных. Она происходит, когда одна транзакция (Т1) читает данные, которые были изменены другой транзакцией (Т2), но ещё не были зафиксированы (не прошел COMMIT).

Основной риск: если транзакция Т2 в итоге будет отменена (ROLLBACK), то транзакция Т1 будет работать с неактуальными, "грязными" данными, которые по факту никогда не существовали в базе данных. Это может привести к серьезным ошибкам в логике приложения.

Аналогия: Представьте, что вы читаете черновик документа, над которым работает ваш коллега. Вы делаете выводы на основе этого черновика, но затем коллега решает удалить все свои правки. В итоге ваша работа основана на неверной информации.

В Go это можно проиллюстрировать на примере работы с БД:

// Предполагается, что уровень изоляции установлен в READ UNCOMMITTED
tx1, _ := db.Begin()
tx2, _ := db.Begin()

// Транзакция 1 обновляет баланс, но пока не фиксирует изменения
tx1.Exec("UPDATE accounts SET balance = 100 WHERE id = 1")

// Транзакция 2 читает НЕзафиксированные (грязные) данные
var balance int
tx2.QueryRow("SELECT balance FROM accounts WHERE id = 1").Scan(&balance)
fmt.Println(balance) // Выведет 100

// Транзакция 1 откатывает изменения
tx1.Rollback()

// Теперь в базе данных баланс остался прежним, но транзакция tx2 уже
// успела прочитать неверное значение 100 и может использовать его в дальнейших операциях.

Такое поведение возможно только при самом низком уровне изоляции транзакций — READ UNCOMMITTED. Чтобы избежать грязного чтения, используют более строгие уровни, такие как READ COMMITTED (по умолчанию во многих СУБД, например, в PostgreSQL), REPEATABLE READ или SERIALIZABLE.

Ответ 18+ 🔞

А, ну это же классика, блядь! Грязное чтение, ёпта! Это как заглянуть в чужой черновик, пока тот мудак ещё думает, что он там гениальное написал, а потом он этот черновик — хвать! — и в урну. А ты уже на его основе доклад начал готовить, пиздец.

Представь, два потока в базе данных, как два мудака за одним компом. Первый, Т1, сидит, обновляет баланс счёта. Написал UPDATE, но ещё не нажал свою жирную кнопку COMMIT, блядь. Данные-то в базе уже поменялись, но они как бы в подвешенном состоянии, в рот меня чих-пых!

И тут второй, Т2, такой хитрожопый, подкрадывается на уровне изоляции READ UNCOMMITTED и считывает эти самые сырые, неутверждённые данные. Прочитал, обрадовался: «О, баланс сотка!». И пошёл себе дальше, строить логику на этом.

А первый, Т1, в это время смотрит на свои изменения, чешет репу и думает: «Да ну нахуй, что-то я накосячил». И — бац! — ROLLBACK. Все его правки откатываются, как будто их и не было. А у второго уже в голове и в коде засела эта самая «сотка», которой никогда не существовало! Вот это и есть грязное чтение, ёбана. Чтение фантомных, блядь, данных.

Вот смотри, на Go это выглядит примерно так, сука:

// Допустим, уровень изоляции стоит самый распиздяйский — READ UNCOMMITTED
tx1, _ := db.Begin()
tx2, _ := db.Begin()

// Первый мудак (tx1) меняет баланс, но не фиксирует
tx1.Exec("UPDATE accounts SET balance = 100 WHERE id = 1")

// Второй мудак (tx2) уже лезет читать эту неконченую хуйню
var balance int
tx2.QueryRow("SELECT balance FROM accounts WHERE id = 1").Scan(&balance)
fmt.Println(balance) // Напечатает 100, ебать его в сраку!

// А тут первый передумывает и откатывается
tx1.Rollback()

// И теперь в базе лежит старое значение, но второй-то уже прочитал сотку!
// И сидит теперь с ней, как дурак, и дальше с ней работает. Пиздец, да?

Чтобы такого пиздеца не было, надо уровни изоляции повыше ставить. Хотя бы READ COMMITTED, который, кстати, во многих базах по умолчанию. Тогда второй поток будет ждать, пока первый не закончит свои дела — либо закоммитит, либо откатит — и только потом прочитает уже нормальные, чистые данные. А то так и до ебалы программной дойти можно, в рот меня чих-пых!