Какой уровень изоляции транзакций по умолчанию в Spring?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Уровень изоляции транзакций в Spring по умолчанию
Уровень изоляции транзакций в Spring по умолчанию — ISOLATION_DEFAULT, который использует уровень изоляции базы данных по умолчанию. Для большинства БД это READ_COMMITTED.
Значение ISOLATION_DEFAULT
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Transactional // ISOLATION_DEFAULT (по умолчанию)
public void myMethod() {
// Использует isolation уровень БД по умолчанию
}
@Transactional(
isolation = Isolation.DEFAULT // Явное указание DEFAULT
)
public void explicitDefault() {
// Эквивалентно предыдущему методу
}
Уровни изоляции в Spring
Spring предоставляет перечисление Isolation с пятью уровнями:
public enum Isolation {
DEFAULT(-1), // Уровень БД по умолчанию
READ_UNCOMMITTED(1), // Самый низкий уровень (грязные чтения)
READ_COMMITTED(2), // Средний уровень (большинство БД по умолчанию)
REPEATABLE_READ(4), // Высокий уровень
SERIALIZABLE(8); // Самый высокий уровень (полная изоляция)
}
Четыре уровня изоляции ACID
1. READ_UNCOMMITTED (Самый низкий — уровень 0)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readUncommittedExample() {
// Проблемы:
// 1. Dirty Reads (грязные чтения)
// 2. Non-repeatable Reads
// 3. Phantom Reads
}
// Пример: Dirty Read
Transaction 1: Transaction 2:
SELECT balance FROM account
// Может прочитать незакоммиченное изменение
UPDATE account SET balance = 500
(Не закоммичено ещё)
// Видит balance = 500 (грязное чтение!)
SELECT balance FROM account
ROLLBACK
// balance вернулся на старое значение
// но Транзакция 2 уже использовала 500
2. READ_COMMITTED (По умолчанию для большинства БД)
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readCommittedExample() {
// Предотвращает: Dirty Reads ✓
// НО позволяет: Non-repeatable Reads, Phantom Reads
}
// Пример: Non-repeatable Read
Transaction 1: Transaction 2:
SELECT balance FROM account
// Результат: 1000
UPDATE account SET balance = 500
COMMIT
SELECT balance FROM account
// Результат: 500 (Изменилось во время транзакции T1!)
// Это non-repeatable read - одно SELECT вернул разные результаты
3. REPEATABLE_READ (Для PostgreSQL, MySQL с InnoDB)
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void repeatableReadExample() {
// Предотвращает: Dirty Reads ✓, Non-repeatable Reads ✓
// НО позволяет: Phantom Reads
}
// Пример: Phantom Read
Transaction 1: Transaction 2:
SELECT * FROM orders WHERE status=pending
// Результат: 5 заказов
INSERT INTO orders VALUES (6, pending)
COMMIT
SELECT * FROM orders WHERE status=pending
// Результат: 6 заказов! (Появился новый заказ - phantom read)
4. SERIALIZABLE (Самый высокий)
@Transactional(isolation = Isolation.SERIALIZABLE)
public void serializableExample() {
// Предотвращает: Все проблемы ✓
// Но: Наибольшее снижение производительности
// Транзакции выполняются как будто последовательно
}
// Полная изоляция - выполняется как монопоточно
Transaction 1: Transaction 2:
BEGIN
BEGIN (ждёт завершения T1)
SELECT balance FROM account
// Может читать/писать
COMMIT
// Теперь T2 может начать
SELECT balance FROM account
Матрица проблем и уровней изоляции
| Проблема | Desc | READ_UNCOMMITTED | READ_COMMITTED | REPEATABLE_READ | SERIALIZABLE |
|---|---|---|---|---|---|
| Dirty Read | Чтение незакоммиченных изменений | ✗ | ✓ | ✓ | ✓ |
| Non-repeatable Read | Одно SELECT вернул разные результаты | ✗ | ✗ | ✓ | ✓ |
| Phantom Read | Новые строки появились во время транзакции | ✗ | ✗ | ✗ | ✓ |
Какой level используется по умолчанию в разных БД?
MySQL (InnoDB) → REPEATABLE_READ
PostgreSQL → READ_COMMITTED
Oracle → READ_COMMITTED
SQL Server → READ_COMMITTED
SQLite → SERIALIZABLE (по типу)
Поэтому, когда ты используешь @Transactional без указания isolation, Spring берёт значение по умолчанию из БД:
// На PostgreSQL это будет READ_COMMITTED
// На MySQL это будет REPEATABLE_READ
@Transactional
public void defaultIsolation() {
// ...
}
Пример: Явное указание уровня изоляции
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Isolation;
@Service
public class BankService {
// Низкий уровень (риск грязных чтений, но быстро)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void fastButRiskyOperation() {
// Используется когда скорость важнее точности
}
// По умолчанию (компромисс между скоростью и безопасностью)
@Transactional(isolation = Isolation.READ_COMMITTED)
public void normalOperation() {
// Рекомендуется для большинства приложений
}
// Высокий уровень (гарантирует консистентность, но медленно)
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void safeLongRunningOperation() {
// Используется для критичных операций
}
// Максимальная безопасность (но с риском deadlock-ов)
@Transactional(isolation = Isolation.SERIALIZABLE)
public void criticalFinancialOperation() {
// Используется РЕДКО - даже в финансовых системах
}
}
Практический пример: Банковская операция
@Service
public class TransferService {
@Autowired
private AccountRepository accountRepository;
// ✓ Правильно для transfer: READ_COMMITTED недостаточна!
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
// Читаем баланс
Account from = accountRepository.findById(fromId);
Account to = accountRepository.findById(toId);
// Проверяем баланс
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
}
// UPDATE-ы
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
accountRepository.save(from);
accountRepository.save(to);
// С REPEATABLE_READ:
// - Другой поток не может изменить баланс между SELECT и UPDATE
// - Гарантируется консистентность денег
}
}
Spring Configuration для всех транзакций
import org.springframework.context.annotation.Bean;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
// По умолчанию: READ_COMMITTED
@Bean
public PlatformTransactionManager transactionManager(
DataSource dataSource
) {
return new DataSourceTransactionManager(dataSource);
}
}
Проверка текущего уровня изоляции
@Transactional
public void checkIsolationLevel() {
Connection conn = // получить connection
int isolationLevel = conn.getTransactionIsolation();
switch (isolationLevel) {
case Connection.TRANSACTION_READ_UNCOMMITTED:
System.out.println("READ_UNCOMMITTED");
break;
case Connection.TRANSACTION_READ_COMMITTED:
System.out.println("READ_COMMITTED"); // Вероятно, это
break;
case Connection.TRANSACTION_REPEATABLE_READ:
System.out.println("REPEATABLE_READ");
break;
case Connection.TRANSACTION_SERIALIZABLE:
System.out.println("SERIALIZABLE");
break;
}
}
Best Practices
- Для большинства приложений: используй
ISOLATION_DEFAULT(READ_COMMITTED) - Для критичных операций: используй
REPEATABLE_READилиSERIALIZABLE - Избегай READ_UNCOMMITTED: грязные чтения очень опасны
- Документируй выбор: почему выбран конкретный уровень
- Тестируй с race conditions: проверяй поведение с параллельными транзакциями
- Помни о deadlock-ах: более высокие уровни → больше блокировок → риск deadlock
Заключение
По умолчанию Spring использует ISOLATION_DEFAULT, который делегирует выбор уровня изоляции базе данных. Для большинства БД это READ_COMMITTED — хороший компромисс между безопасностью и производительностью. Для критичных операций (особенно финансовых) нужно явно указать более высокий уровень изоляции.