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

Что такое Isolation?

2.0 Middle🔥 171 комментариев
#Основы Java

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

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

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

Isolation: Изоляция в ACID свойствах транзакций

Isolation (изоляция) — это третье свойство ACID модели транзакций, которое гарантирует что одновременно выполняющиеся транзакции не влияют друг на друга и каждая транзакция видит консистентное состояние данных. Это критически важный концепт при работе с многопользовательскими базами данных.

ACID свойства

Прежде чем говорить об Isolation, вспомним ACID:

  • A (Atomicity) — атомарность: транзакция либо полностью выполнена, либо полностью откачена
  • C (Consistency) — консистентность: данные переходят из одного консистентного состояния в другое
  • I (Isolation) — изоляция: транзакции не влияют друг на друга
  • D (Durability) — долговечность: зафиксированные данные сохраняются

Определение Isolation

Isolation — это уровень независимости между одновременно выполняющимися транзакциями. Высокая изоляция = меньше конфликтов, но медленнее. Низкая изоляция = быстрее, но больше проблем.

Уровни изоляции транзакций

SQL стандарт определяет 4 уровня изоляции:

1. READ_UNCOMMITTED (самый низкий уровень)

Описание: транзакция может читать незафиксированные данные других транзакций

Проблемы:

  • Dirty Read (чтение грязных данных)
  • Non-Repeatable Read
  • Phantom Read
Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

Statement stmt = conn.createStatement();
conn.setAutoCommit(false);

// Транзакция A
int balance = getBalance(1);  // Может получить незафиксированное значение
conn.commit();

Использование: очень редко, только для статистических отчетов где точность не критична

2. READ_COMMITTED (уровень по умолчанию)

Описание: транзакция может читать только зафиксированные данные

Предотвращает:

  • Dirty Read

Не предотвращает:

  • Non-Repeatable Read
  • Phantom Read
Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Statement stmt = conn.createStatement();
conn.setAutoCommit(false);

// Транзакция A
int balance1 = getBalance(1);  // чтение 1
// ... какие-то операции ...
int balance2 = getBalance(1);  // может отличаться от balance1!
// Non-repeatable read произошла

conn.commit();

Использование: большинство приложений, хороший баланс между производительностью и безопасностью

3. REPEATABLE_READ

Описание: транзакция видит снимок данных на момент её начала

Предотвращает:

  • Dirty Read
  • Non-Repeatable Read

Не предотвращает:

  • Phantom Read
Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

Statement stmt = conn.createStatement();
conn.setAutoCommit(false);

// Транзакция A
int balance1 = getBalance(1);     // чтение 1: 1000
// ... какие-то операции ...
int balance2 = getBalance(1);     // чтение 2: ВСЕГДА 1000
// Даже если другая транзакция изменила и зафиксировала значение

conn.commit();

Проблема — Phantom Read:

// Транзакция A
Statement stmt = conn.createStatement();
conn.setAutoCommit(false);

// Запрос 1
ResultSet rs1 = stmt.executeQuery(
    "SELECT COUNT(*) FROM orders WHERE amount > 1000"
);
rs1.next();
int count1 = rs1.getInt(1);  // 5 заказов

// ... другая транзакция добавляет новый заказ с amount > 1000 и commits ...

// Запрос 2 (тот же запрос)
ResultSet rs2 = stmt.executeQuery(
    "SELECT COUNT(*) FROM orders WHERE amount > 1000"
);
rs2.next();
int count2 = rs2.getInt(1);  // 6 заказов! (Phantom Read)

conn.commit();

Использование: когда нужна стабильность для отдельных строк, например финансовые операции

4. SERIALIZABLE (самый высокий уровень)

Описание: транзакции выполняются как если бы они были серийными

Предотвращает:

  • Все проблемы конкурентности

Стоимость: очень медленно

Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

Statement stmt = conn.createStatement();
conn.setAutoCommit(false);

// Транзакция A
// Все читаемые и изменяемые данные полностью заблокированы
// Другие транзакции не могут ни читать, ни писать в эти данные

conn.commit();

Использование: критичные операции (например, финансовые расчеты большого объема)

Таблица проблем изоляции

УровеньDirty ReadNon-Repeatable ReadPhantom ReadПроизводительность
READ_UNCOMMITTEDДАДАДА⭐⭐⭐⭐⭐
READ_COMMITTEDНЕТДАДА⭐⭐⭐⭐
REPEATABLE_READНЕТНЕТДА⭐⭐⭐
SERIALIZABLEНЕТНЕТНЕТ⭐⭐

Пример: Банковская система

public class BankTransferService {
    
    // Низкий уровень изоляции: не подходит для операций с деньгами
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void unsafeTransfer(int fromId, int toId, double amount) {
        // ОПАСНО! Могут быть несогласованности
    }
    
    // Стандартный уровень: подходит для большинства операций
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void standardTransfer(int fromId, int toId, double amount) {
        Account from = accountRepository.findById(fromId);
        Account to = accountRepository.findById(toId);
        
        if (from.getBalance() >= amount) {
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
    
    // Высокий уровень: гарантирует консистентность
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void safeTransfer(int fromId, int toId, double amount) {
        // Используем FOR UPDATE для блокировки
        Account from = accountRepository.findByIdWithLock(fromId);
        Account to = accountRepository.findByIdWithLock(toId);
        
        if (from.getBalance() >= amount) {
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
}

Spring Data JPA примеры

public interface AccountRepository extends JpaRepository<Account, Long> {
    
    // Явное блокирование для чтения
    @Lock(LockModeType.PESSIMISTIC_READ)
    @Query("SELECT a FROM Account a WHERE a.id = :id")
    Account findByIdForRead(@Param("id") Long id);
    
    // Явное блокирование для записи
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT a FROM Account a WHERE a.id = :id")
    Account findByIdForWrite(@Param("id") Long id);
    
    // Оптимистичное блокирование (используется @Version)
    @Lock(LockModeType.OPTIMISTIC)
    Account findByIdOptimistic(Long id);
}

@Entity
public class Account {
    @Id
    private Long id;
    private double balance;
    
    @Version  // для оптимистичного блокирования
    private Long version;
}

Практический пример с разными уровнями

@Service
public class OrderService {
    
    // Для чтения статистики: READ_UNCOMMITTED OK
    @Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
    public OrderStats getStats() {
        return new OrderStats(
            orderRepository.count(),
            orderRepository.getTotalRevenue()
        );
    }
    
    // Для обновления статуса: READ_COMMITTED обычно OK
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void updateStatus(Long orderId, OrderStatus status) {
        Order order = orderRepository.findById(orderId).orElseThrow();
        order.setStatus(status);
        orderRepository.save(order);
    }
    
    // Для финансовых операций: REPEATABLE_READ или SERIALIZABLE
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void completeOrder(Long orderId) {
        Order order = orderRepository.findById(orderId).orElseThrow();
        
        // Пересчитываем цену
        double price = recalculatePrice(order);
        
        // Проверяем наличие
        for (LineItem item : order.getItems()) {
            if (!checkInventory(item)) {
                throw new InsufficientInventoryException();
            }
        }
        
        order.setStatus(OrderStatus.COMPLETED);
        orderRepository.save(order);
    }
}

Оптимистичное vs Пессимистичное блокирование

Пессимистичное (Pessimistic Locking):

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT a FROM Account a WHERE a.id = :id")
Account findForUpdate(@Param("id") Long id);

// БД блокирует строку
// Конфликты невозможны, но производительность ниже

Оптимистичное (Optimistic Locking):

@Entity
public class Account {
    @Id
    private Long id;
    
    @Version  // Версия автоматически инкрементируется
    private Long version;
    
    private double balance;
}

// Если версия не совпадает при сохранении -> OptimisticLockException

Лучшие практики

  1. Используйте READ_COMMITTED по умолчанию — хороший баланс
  2. Для финансовых операций минимум REPEATABLE_READ
  3. SERIALIZABLE только для критичных операций — очень медленно
  4. Используйте блокирование явно где нужно через FOR UPDATE или @Lock
  5. Минимизируйте время транзакции — чем дольше открыта, тем больше конфликтов
  6. Тестируйте с реальной нагрузкой — проблемы конкурентности проявляются под нагрузкой
  7. Мониторьте deadlocks в production

Pонимание Isolation критично для разработки надежных многопользовательских приложений.

Что такое Isolation? | PrepBro