← Назад к вопросам
Зачем нужны уровни изоляций @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 Read | Non-Rep Read | Phantom | Используется |
|---|---|---|---|---|
| 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
- Используй READ_COMMITTED по умолчанию (более быстро, достаточно безопасно)
- SERIALIZABLE только для финансов (переводы, платежи)
- REPEATABLE_READ для отчётов (консистентные снимки)
- Избегай READ_UNCOMMITTED (почти не используется)
- Тестируй на реальной нагрузке (проблемы видны под нагрузкой)
Заключение
Уровни изоляции нужны для:
- Защиты от race conditions при конкурентном доступе
- Баланса между скоростью и безопасностью
- Соответствия требованиям приложения (финансы требуют SERIALIZABLE)
- Оптимизации производительности (не все нужны максимум изоляции)
- Консистентности данных (ACID гарантии)