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

Зачем нужны уровни изоляций @Transactional?

3.0 Senior🔥 81 комментариев
#Базы данных и SQL

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

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

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

# Зачем нужны уровни изоляций @Transactional?

Уровни изоляции (@Transactional(isolation=...)) определяют как транзакции видят изменения друг друга и защищают от race conditions в многопоточной среде.

4 уровня изоляции (ACID)

1. READ_UNCOMMITTED (самый слабый)

Транзакция может читать НЕCOMMITTED данные (dirty reads).

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void unsafeRead() { ... }

Проблемы:

  • Dirty read: читаешь незавершённую транзакцию
  • Non-repeatable read
  • Phantom reads

Когда использовать: редко (почти никогда)

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

Транзакция читает только COMMITTED данные.

@Transactional(isolation = Isolation.READ_COMMITTED)
public void safeRead() { ... }

Проблемы:

  • Non-repeatable read: данные меняются внутри транзакции
  • Phantom reads: новые строки появляются

Когда использовать: большинство случаев

3. REPEATABLE_READ

Транзакция видит консистентный снимок данных.

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void consistentRead() { ... }

Преимущества:

  • Нет dirty reads
  • Нет non-repeatable reads
  • Остаются phantom reads

Когда использовать: когда нужна консистентность внутри транзакции

4. SERIALIZABLE (самый сильный)

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

@Transactional(isolation = Isolation.SERIALIZABLE)
public void fullyIsolated() { ... }

Преимущества:

  • Полная изоляция
  • Нет никаких проблем с конкурентностью

Недостатки:

  • Медленнее всего
  • Может быть deadlock

Когда использовать: критичные финансовые операции

Проблемы конкурентности

Dirty Read

Транзакция 1               Транзакция 2
├─ UPDATE balance = 500   │
│ (не COMMITTED)          │
└─ (в процессе)          ├─ READ_UNCOMMITTED
                          ├─ Читает balance = 500 ← Грязное
                          │  (еще не сохранено)
                          └─
├─ ROLLBACK              │
balance остается = 1000   │

Транзакция 2 прочитала НЕСУЩЕСТВУЮЩИЕ данные!

Non-repeatable Read

Транзакция 1               Транзакция 2
├─ READ balance = 1000   │
├─ (в процессе)          ├─ UPDATE balance = 500
├─ COMMIT                │
│                         ├─ COMMIT
└─ READ balance ≠ 1000   │
   (теперь 500)          └─

Одна и та же переменная имеет разные значения!

Phantom Read

Транзакция 1               Транзакция 2
├─ COUNT(*) = 10         │
├─ (в процессе)          ├─ INSERT (новая строка)
├─                        ├─ COMMIT
├─ COUNT(*) ≠ 10         │
   (теперь 11)           └─

Таблица уровней

УровеньDirty ReadNon-Rep ReadPhantomИспользуется
READ_UNCOMMITTEDРедко
READ_COMMITTEDЧасто (default)
REPEATABLE_READСредне
SERIALIZABLEКритичные данные

Примеры использования

Обычная операция

@Transactional(isolation = Isolation.READ_COMMITTED)
public void normalOperation(Long userId) {
    User user = userRepository.findById(userId).orElseThrow();
    user.setName("John");
    userRepository.save(user);
}

Критичная операция

@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
    Account from = accountRepository.findById(fromId).orElseThrow();
    Account to = accountRepository.findById(toId).orElseThrow();
    
    from.setBalance(from.getBalance().subtract(amount));
    to.setBalance(to.getBalance().add(amount));
    
    accountRepository.save(from);
    accountRepository.save(to);
}

Отчёты (только чтение)

@Transactional(isolation = Isolation.READ_COMMITTED, readOnly = true)
public List<ReportData> generateReport() {
    return reportRepository.findAll();
}

Performance vs Safety

RISK (много проблем)         PERFORMANCE (быстро)
    ↑
    |
    ├─ SERIALIZABLE    ← Максимальная безопасность
    ├─ REPEATABLE_READ
    ├─ READ_COMMITTED  ← Баланс (рекомендуется)
    └─ READ_UNCOMMITTED ← Максимальная скорость
    ↓
PERFORMANCE (медленно)      SAFETY (мало проблем)

Best Practices

  1. Используй READ_COMMITTED по умолчанию (более быстро, достаточно безопасно)
  2. SERIALIZABLE только для финансов (переводы, платежи)
  3. REPEATABLE_READ для отчётов (консистентные снимки)
  4. Избегай READ_UNCOMMITTED (почти не используется)
  5. Тестируй на реальной нагрузке (проблемы видны под нагрузкой)

Заключение

Уровни изоляции нужны для:

  1. Защиты от race conditions при конкурентном доступе
  2. Баланса между скоростью и безопасностью
  3. Соответствия требованиям приложения (финансы требуют SERIALIZABLE)
  4. Оптимизации производительности (не все нужны максимум изоляции)
  5. Консистентности данных (ACID гарантии)