Опишите стандартные уровни изоляции транзакций в базах данных.

Ответ

Уровни изоляции транзакций — это настройка в СУБД, которая определяет, насколько одна транзакция "видит" изменения, сделанные в параллельных транзакциях. Стандарт SQL определяет четыре основных уровня:

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

    • Описание: Самый низкий уровень. Транзакция может читать данные, которые были изменены другой транзакцией, но еще не зафиксированы (не применены через COMMIT).
    • Проблема: Позволяет "грязное чтение" (dirty read).
  2. Read Committed (Чтение зафиксированных данных)

    • Описание: Транзакция видит только те изменения, которые были зафиксированы другими транзакциями. Это уровень по умолчанию для многих СУБД (например, PostgreSQL, Oracle).
    • Проблема: Защищает от "грязного чтения", но возможна проблема "неповторяемого чтения" (non-repeatable read) — повторное чтение одной и той же строки в рамках одной транзакции может вернуть разные данные.
  3. Repeatable Read (Повторяемое чтение)

    • Описание: Гарантирует, что если транзакция повторно читает одну и ту же строку, она получит те же самые данные. СУБД блокирует читаемые строки от изменений другими транзакциями.
    • Проблема: Защищает от "неповторяемого чтения", но возможна проблема "фантомного чтения" (phantom read) — когда при повторном чтении диапазона строк появляются новые строки, добавленные другой транзакцией.
  4. Serializable (Сериализуемый)

    • Описание: Самый строгий уровень. Гарантирует, что результат параллельного выполнения транзакций будет таким же, как если бы они выполнялись последовательно. Защищает от всех вышеперечисленных проблем.
    • Цена: Наибольшее влияние на производительность из-за использования блокировок.

Установка уровня изоляции в Go

В Go уровень изоляции указывается при старте транзакции с помощью объекта sql.TxOptions. Неверно думать, что он устанавливается на всё соединение.

package main

import (
    "context"
    "database/sql"
    "log"
)

func main() {
    db, err := sql.Open("postgres", "your_connection_string")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    ctx := context.Background()

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

    // ... выполняем операции в транзакции ...

    // Завершаем транзакцию
    if err := tx.Commit(); err != nil {
        log.Fatal(err) // или tx.Rollback()
    }
}