Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Уровень изоляции транзакций в базах данных
Ответ
SERIALIZABLE — это самый высокий уровень изоляции транзакций.
Он гарантирует, что транзакции выполняются так, как если бы они были выполнены последовательно, один за другим, без перекрытия.
4 уровня изоляции (ACID)
По возрастанию уровня: READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE
1. READ UNCOMMITTED (самый низкий)
Разрешает все проблемы:
- Dirty reads (грязное чтение)
- Non-repeatable reads
- Phantom reads
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void processTransaction() {
// Транзакция может прочитать незафиксированные изменения из других транзакций
User user = userRepository.findById(1L);
// user может содержать данные, которые ещё не committed
}
// Редко используется, очень опасно
// SQL пример:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Проблема: Dirty Read
Транзакция A | Транзакция B
─────────────────────────────────────
BEGIN TRANSACTION; |
UPDATE user SET
balance = 50 |
WHERE id = 1; |
| BEGIN TRANSACTION;
| SELECT balance FROM user WHERE id = 1;
| -- Видит 50 (незафиксированное значение!)
| COMMIT;
ROLLBACK; |
-- Откатили, balance = 100|
-- Но B уже прочитал 50!
2. READ COMMITTED (по умолчанию в большинстве БД)
Разрешает только:
- Non-repeatable reads
- Phantom reads
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processTransaction() {
// По умолчанию в PostgreSQL, SQL Server
// Вы видите только зафиксированные данные
User user = userRepository.findById(1L);
}
// SQL пример:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
SELECT * FROM users WHERE id = 1;
-- видит только committed данные
COMMIT;
Проблема: Non-repeatable Read
Транзакция A | Транзакция B
─────────────────────────────────────
BEGIN TRANSACTION; |
SELECT balance FROM user |
WHERE id = 1; |
-- Видит: 100 |
| BEGIN TRANSACTION;
| UPDATE user SET balance = 50 WHERE id = 1;
| COMMIT;
SELECT balance FROM user |
WHERE id = 1; |
-- Видит: 50 (изменилось!)
-- Один и тот же SELECT дал разные результаты
COMMIT;
3. REPEATABLE READ (MySQL по умолчанию)
Разрешает только:
- Phantom reads
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processTransaction() {
// Один SELECT всегда вернёт одинаковый результат в границах транзакции
List<User> users = userRepository.findAll();
// Если другая транзакция добавит новый user после этого SELECT,
// мы не увидим его (кроме как в следующем SELECT)
}
// SQL пример:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT * FROM users WHERE age > 18;
-- SELECT вернёт X рядов
-- Даже если другая транзакция добавит/удалит юзеров,
-- эти изменения не будут видны в этом SELECT
COMMIT;
Проблема: Phantom Read
Транзакция A | Транзакция B
─────────────────────────────────────
BEGIN TRANSACTION; |
SELECT COUNT(*) FROM users|
WHERE status='ACTIVE'; |
-- Результат: 5 |
| BEGIN TRANSACTION;
| INSERT INTO users (status) VALUES ('ACTIVE');
| COMMIT;
SELECT COUNT(*) FROM users|
WHERE status='ACTIVE'; |
-- Результат: 6 (новая строка появилась!)
COMMIT;
4. SERIALIZABLE (самый высокий)
Не разрешает никакие проблемы:
- Нет dirty reads
- Нет non-repeatable reads
- Нет phantom reads
@Transactional(isolation = Isolation.SERIALIZABLE)
public void processTransaction() {
// Полная изоляция
List<User> users = userRepository.findAll();
// Никаких изменений в других транзакциях не влияют
// Транзакции выполняются как if they were serial
}
// SQL пример:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
SELECT * FROM users WHERE status='ACTIVE';
UPDATE users SET balance = balance - 100 WHERE id = 5;
COMMIT;
-- Во время этой транзакции никакая другая транзакция не может
-- ни читать, ни писать данные, затронутые этой транзакцией
Механизм SERIALIZABLE
Range locks (блокировки диапазонов):
public class OrderService {
@Transactional(isolation = Isolation.SERIALIZABLE)
public void createOrder(CreateOrderRequest request) {
// 1. Читаем товары в диапазоне (price > 100)
List<Product> expensive = productRepository.findByPriceGreaterThan(100);
// 2. Вся таблица товаров ЗАБЛОКИРОВАНА для других транзакций
// Никто не может:
// - Изменять товары
// - Добавлять товары
// - Удалять товары
// - Читать те же товары
// 3. Создаём заказ
Order order = new Order(expensive);
orderRepository.save(order);
// 4. Блокировка снимается при COMMIT
}
}
Таблица изоляции
| Уровень | Dirty Read | Non-rep. Read | Phantom Read | Производительность | Использование |
|---|---|---|---|---|---|
| READ UNCOMMITTED | Да | Да | Да | Высокая | Редко |
| READ COMMITTED | Нет | Да | Да | Хорошая | Default большинства БД |
| REPEATABLE READ | Нет | Нет | Да | Средняя | MySQL default |
| SERIALIZABLE | Нет | Нет | Нет | Низкая | Критичные транзакции |
Практические примеры
SERIALIZABLE для финансовых операций:
@Service
public class PaymentService {
@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
// Полная изоляция от других платежей
User from = userRepository.findById(fromUserId).orElseThrow();
User to = userRepository.findById(toUserId).orElseThrow();
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
}
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
userRepository.save(from);
userRepository.save(to);
transactionRepository.save(new Transaction(from, to, amount));
}
}
READ COMMITTED для обычных операций:
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<Product> getProductsByCategory(String category) {
// Обычная бизнес-логика
// Видим committed данные
// Производительность нормальная
return productRepository.findByCategory(category);
}
Трейд-офф
SERIALIZABLE:
- Максимальная безопасность данных
- Минимальная конкурентность (потоки ждут)
- Риск deadlock-ов
- Может быть медленнее в 10-100 раз
READ COMMITTED:
- Хороший баланс
- Хорошая конкурентность
- Приемлемый уровень безопасности
- Нужно обрабатывать non-repeatable reads в коде
Вывод
SERIALIZABLE — самый высокий уровень изоляции. Используется редко из-за performance penalty, но критичен для:
- Финансовых систем
- Платежных шлюзов
- Критичных бизнес-операций
- Когда data consistency важнее, чем throughput