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

Что такое изолированность в БД?

2.3 Middle🔥 141 комментариев
#Базы данных и SQL

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

# Изолированность (Isolation) в базах данных

Изолированность — это один из четырех ключевых свойств транзакций (ACID), который гарантирует, что конкурентные транзакции не влияют друг на друга. Это означает, что каждая транзакция выполняется так, как будто она единственная в системе.

Что такое ACID?

ACID — это аббревиатура из четырех критических свойств:

  • Atomicity (Атомарность) — транзакция либо полностью выполнена, либо полностью отменена
  • Consistency (Согласованность) — данные переходят из одного консистентного состояния в другое
  • Isolation (Изолированность) — конкурентные транзакции не взаимодействуют
  • Durability (Долговечность) — завершенная транзакция сохраняется

Мы рассмотрим именно Isolation.

Почему нужна изолированность?

В многопользовательской системе несколько транзакций могут одновременно обращаться к одним и тем же данным. Без изолированности возникают проблемы:

Проблема 1: Грязное чтение (Dirty Read)

Транзакция 1              Транзакция 2
└─ UPDATE balance = 900
                          └─ READ balance (900) ❌ Грязные данные!
Транзакция 1 ROLLBACK
(balance = 1000)          └─ Использует неверное значение 900

Транзакция 2 прочитала данные, которые потом были откачены.

Проблема 2: Неповторяющееся чтение (Non-repeatable Read)

Транзакция 1              Транзакция 2
└─ READ balance = 1000
                          └─ UPDATE balance = 800
                          └─ COMMIT
└─ READ balance = 800 ❌ Другое значение!

Одна и та же строка при повторном чтении имеет разные значения.

Проблема 3: Фантомное чтение (Phantom Read)

Транзакция 1                      Транзакция 2
└─ SELECT COUNT(*) FROM orders    (5 заказов)
                                  └─ INSERT INTO orders (...)
                                  └─ COMMIT
└─ SELECT COUNT(*) FROM orders    (6 заказов!) ❌ Фантомная строка

Строка появилась из-за другой транзакции.

Уровни изолированности

Большинство СУБД поддерживают 4 стандартных уровня (определены SQL Standard):

1. READ UNCOMMITTED (Грязное чтение разрешено)

Наименее строгий уровень.

// Java/JDBC
Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

Проблемы:

  • Грязное чтение ✓ (возможно)
  • Неповторяющееся чтение ✓ (возможно)
  • Фантомное чтение ✓ (возможно)

Когда использовать: Никогда в продакшене. Только для отчетов, где приблизительность допустима.

2. READ COMMITTED (По умолчанию в большинстве БД)

Среднее значение между производительностью и надежностью.

conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Проблемы:

  • Грязное чтение ✗ (невозможно)
  • Неповторяющееся чтение ✓ (возможно)
  • Фантомное чтение ✓ (возможно)

Механизм: Транзакция может читать только закомиченные данные.

// Пример: Перевод денег
public void transferMoney(long fromAccountId, long toAccountId, BigDecimal amount) {
    // Рассчет баланса будет читать только закомиченные значения
    BigDecimal balance = getBalance(fromAccountId); // READ COMMITTED
    if (balance.compareTo(amount) >= 0) {
        updateBalance(fromAccountId, balance.subtract(amount));
        updateBalance(toAccountId, balance.add(amount));
    }
}

3. REPEATABLE READ (PostgreSQL по умолчанию)

Более строгий, чем READ COMMITTED.

conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

Проблемы:

  • Грязное чтение ✗ (невозможно)
  • Неповторяющееся чтение ✗ (невозможно)
  • Фантомное чтение ✓ (возможно в MySQL)

Механизм: Используются снимки данных (snapshots). Транзакция видит данные такими, какими они были в момент начала транзакции.

// Пример: Проверка ограничения
public void createOrder(long customerId) {
    // Обе SELECT видят данные на момент старта транзакции
    int activeOrders = countActiveOrders(customerId); // Видит снимок
    // ...
    int activeOrders2 = countActiveOrders(customerId); // Тот же снимок
    // activeOrders == activeOrders2 гарантировано
}

4. SERIALIZABLE (Самый строгий)

Полная изолированность, как если бы транзакции выполнялись последовательно.

conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

Проблемы:

  • Грязное чтение ✗ (невозможно)
  • Неповторяющееся чтение ✗ (невозможно)
  • Фантомное чтение ✗ (невозможно)

Стоимость: Значительное снижение производительности из-за большого количества блокировок.

// Пример: Критичные финансовые операции
public void processPayment(long paymentId, BigDecimal amount) {
    // Абсолютная изолированность - никакие другие транзакции
    // не будут видеть несогласованное состояние
    Payment payment = getPayment(paymentId);
    if (payment.getStatus() == PENDING) {
        charge(amount);
        payment.setStatus(COMPLETED);
        updatePayment(payment);
    }
}

Сравнительная таблица

УровеньГрязное чтениеНеповторяющеесяФантомноеПроизводительность
READ UNCOMMITTEDДаДаДаОчень быстро
READ COMMITTEDНетДаДаБыстро
REPEATABLE READНетНетДа/Нет*Медленно
SERIALIZABLEНетНетНетОчень медленно

*В PostgreSQL фантомные чтения невозможны на этом уровне благодаря MVCC.

Практический пример в Java

class BankService {
    
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void transferMoney(long from, long to, BigDecimal amount) {
        // Используется READ COMMITTED (по умолчанию)
        Account fromAccount = accountRepository.findById(from);
        BigDecimal balance = fromAccount.getBalance();
        
        if (balance.compareTo(amount) >= 0) {
            fromAccount.setBalance(balance.subtract(amount));
            
            Account toAccount = accountRepository.findById(to);
            toAccount.setBalance(toAccount.getBalance().add(amount));
            
            accountRepository.save(fromAccount);
            accountRepository.save(toAccount);
        }
    }
    
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void processCheckout(long orderId) {
        // Для критичной операции нужна полная изолированность
        Order order = orderRepository.findById(orderId);
        order.setStatus(PROCESSING);
        orderRepository.save(order);
    }
}

Как выбрать уровень?

  1. По умолчанию: READ COMMITTED

    • Оптимален для большинства приложений
    • Хороший баланс между надежностью и производительностью
  2. REPEATABLE READ, если:

    • Нужна согласованность данных в пределах транзакции
    • Требуется несколько SELECT с гарантией видения одного снимка
  3. SERIALIZABLE, только если:

    • Критичные финансовые операции
    • Требуется абсолютная гарантия консистентности
    • Готовы пожертвовать производительностью
  4. READ UNCOMMITTED - НИКОГДА в продакшене

Выводы: Изолированность — это критическое свойство в многопользовательской среде. Правильный выбор уровня изолированности определяет как надежность системы, так и её производительность. Большинству приложений достаточно READ COMMITTED, но нужно понимать когда требуется более строгий уровень.