Что такое уровни изоляции транзакций и какие аномалии они предотвращают?

Ответ

Уровни изоляции транзакций — это настройка в СУБД, которая определяет, насколько одна транзакция "видит" изменения, сделанные другими параллельными транзакциями. Чем выше уровень изоляции, тем надежнее защита от аномалий, но ниже производительность из-за использования блокировок.

Стандарт SQL определяет четыре основных уровня:

  1. Read Uncommitted (Чтение незафиксированных данных)

    • Что делает: Транзакция может читать данные, которые были изменены другой транзакцией, но еще не зафиксированы (не выполнен COMMIT).
    • Какие аномалии возможны: Грязное чтение (Dirty Read), неповторяющееся чтение (Non-Repeatable Read), фантомное чтение (Phantom Read).
  2. Read Committed (Чтение зафиксированных данных)

    • Что делает: Транзакция видит только те изменения, которые были зафиксированы другими транзакциями до ее начала.
    • Какие аномалии предотвращает: Грязное чтение.
    • Какие аномалии возможны: Неповторяющееся чтение, фантомное чтение.
    • Это уровень по умолчанию во многих СУБД, например, в PostgreSQL и Oracle.
  3. Repeatable Read (Повторяемое чтение)

    • Что делает: Гарантирует, что если одна и та же строка читается несколько раз в рамках одной транзакции, результат будет одинаковым.
    • Какие аномалии предотвращает: Грязное чтение, неповторяющееся чтение.
    • Какие аномалии возможны: Фантомное чтение.
    • Это уровень по умолчанию в MySQL (InnoDB).
  4. Serializable (Сериализуемый)

    • Что делает: Полностью изолирует транзакции друг от друга. Результат параллельного выполнения транзакций эквивалентен их последовательному выполнению.
    • Какие аномалии предотвращает: Все: грязное чтение, неповторяющееся чтение и фантомное чтение.
    • Цена: Самый низкий уровень параллелизма и производительности.

В Go уровень изоляции можно указать при создании транзакции с помощью пакета database/sql:

import (
    "context"
    "database/sql"
)

// ... db *sql.DB ...

ctx := context.Background()

// Начинаем транзакцию с уровнем изоляции Serializable
tx, err := db.BeginTx(ctx, &sql.TxOptions{
    Isolation: sql.LevelSerializable,
})
if err != nil {
    // обработка ошибки
}
// ... работа с транзакцией tx ...