Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Изолированность (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);
}
}
Как выбрать уровень?
-
По умолчанию: READ COMMITTED
- Оптимален для большинства приложений
- Хороший баланс между надежностью и производительностью
-
REPEATABLE READ, если:
- Нужна согласованность данных в пределах транзакции
- Требуется несколько SELECT с гарантией видения одного снимка
-
SERIALIZABLE, только если:
- Критичные финансовые операции
- Требуется абсолютная гарантия консистентности
- Готовы пожертвовать производительностью
-
READ UNCOMMITTED - НИКОГДА в продакшене
Выводы: Изолированность — это критическое свойство в многопользовательской среде. Правильный выбор уровня изолированности определяет как надежность системы, так и её производительность. Большинству приложений достаточно READ COMMITTED, но нужно понимать когда требуется более строгий уровень.