← Назад к вопросам

Что такое грязное Dirty Read?

1.7 Middle🔥 201 комментариев
#Базы данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Dirty Read?

Dirty Read («грязное чтение») — это один из классических видов проблем параллельного доступа к данным, возникающий в системах с незавершенными транзакциями. Это ситуация, когда транзакция читает данные, которые были изменены другой транзакцией, но эти изменения ещё не финализированы (не были подтверждены — committed), а значит, могут быть откачены (rolled back). Таким образом, первая транзакция получает «грязные», временные и потенциально невалидные данные.

Контекст и механизм возникновения

Dirty Read возникает, когда нарушается базовое требование ACID-транзакций, в частности, свойство Isolation (изолированность). Идеальная изолированность требует, чтобы параллельные транзакции не влияли друг на друга. Однако в реальных системах для повышения производительности часто используются более низкие уровни изолированности.

Рассмотрим классический пример на SQL:

-- Транзакция 1 (T1)
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- Баланс пользователя 1 теперь уменьшен на 100, но транзакция НЕ завершена.

-- Транзакция 2 (T2) выполняется параллельно на уровне изолированности, допускающем Dirty Read
BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 1;
-- T2 читает НОВОЕ значение баланса (уменьшенное на 100), которое было изменено T1.
COMMIT; -- T2 завершается, фиксируя чтение "грязных" данных.

-- Транзакция 1 (T1) продолжает работу...
-- ... и по какой-то причине выполняется ROLLBACK.
ROLLBACK;
-- Изменение баланса откатывается. Баланс пользователя 1 возвращается к исходному значению.

В результате T2 прочитала и, возможно, использовала значение баланса, которое никогда не существовало как финализированное состояние в системе. Это может привести к логическим ошибкам, некорректным бизнес-вычислениям или даже финансовым потерям.

Dirty Read в Go при работе с базами данных

В Go при использовании драйверов баз данных (например, database/sql с pq для PostgreSQL или go-sql-driver/mysql для MySQL) контроль над этим осуществляется через установку уровня изолированности транзакций.

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("postgres", "user=postgres dbname=test sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }

    // Начало транзакции с явным указанием уровня изолированности.
    // Уровень sql.IsolationLevelReadCommitted предотвращает Dirty Read.
    tx, err := db.BeginTx(context.Background(), &sql.TxOptions{
        Isolation: sql.LevelReadCommitted, // Уровень изолированности: "Read Committed"
        ReadOnly:  false,
    })
    if err != nil {
        log.Fatal(err)
    }

    // В рамках этой транзакции мы будем читать только подтвержденные (committed) данные.
    var balance int
    err = tx.QueryRow("SELECT balance FROM accounts WHERE user_id = $1", 1).Scan(&balance)
    if err != nil {
        tx.Rollback()
        log.Fatal(err)
    }
    fmt.Printf("Баланс (прочитанный без Dirty Read): %d\n", balance)

    err = tx.Commit()
    if err != nil {
        log.Fatal(err)
    }
}

Уровни изолированности и предотвращение Dirty Read

Для предотвращения Dirty Read используются следующие стандартные уровни изолированности (в порядке возрастания строгости):

  • Read Uncommitted — самый низкий уровень. Dirty Read разрешены. В Go соответствует sql.LevelReadUncommitted. Используется крайне редко.
  • Read Committed — гарантирует, что транзакция читает только зафиксированные (committed) данные. Dirty Read предотвращены. Это самый распространенный уровень в многих базах данных (например, дефолтный в PostgreSQL). В Go: sql.LevelReadCommitted.
  • Repeatable Read и Serializable — более высокие уровни, предотвращающие также другие проблемы параллельности, такие как Non-repeatable Read и Phantom Read. Dirty Read на этих уровнях также невозможны.

Ключевые выводы для разработчика на Go

  1. Проблема системного уровня: Dirty Read — это проблема, управляемая на уровне базы данных и её транзакционных настроек, а не на уровне языка Go.
  2. Ответственность разработчика: При написании критически важного бизнес-кода, особенно связанного с финансами или согласованностью данных, разработчик на Go обязан явно задавать соответствующий уровень изолированности транзакций через sql.TxOptions.
  3. Выбор уровня: Уровень Read Committed является балансом между производительностью и корректностью для большинства приложений и эффективно блокирует Dirty Read.
  4. Тестирование: Сложные сценарии с параллельными транзакциями и потенциальными грязными чтениями должны быть частью интеграционного тестирования приложения.

Таким образом, понимание Dirty Read позволяет разработчику на Go сознательно конфигурировать транзакционную работу с базами данных, обеспечивая корректность и надежность данных в многопользовательском окружении.