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

Что такое пессимистичная транзакция?

2.7 Senior🔥 141 комментариев
#ORM и Hibernate

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

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

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

Что такое пессимистичная транзакция?

Пессимистичная транзакция (pessimistic locking) — это механизм контроля конкурентного доступа, при котором система предполагает, что конфликты между транзакциями произойдут часто, и поэтому блокирует ресурсы до начала работы с ними. Это противоположно оптимистичному подходу, который предполагает редкие конфликты.

Основной принцип

Пессимистичная блокировка работает по следующей схеме:

1. Транзакция 1: SELECT FOR UPDATE (блокирует строку)
2. Транзакция 2: пытается SELECT FOR UPDATE (ждёт освобождения)
3. Транзакция 1: UPDATE, COMMIT (разблокирует строку)
4. Транзакция 2: получает доступ

Использование в JDBC и Hibernate

// Чистый SQL с пессимистичной блокировкой
String sql = "SELECT * FROM accounts WHERE id = ? FOR UPDATE";
Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setLong(1, accountId);
ResultSet rs = stmt.executeQuery();

if (rs.next()) {
    // Ресурс заблокирован, другие транзакции не могут получить FOR UPDATE
    int balance = rs.getInt("balance");
    
    // UPDATE запрос
    String updateSql = "UPDATE accounts SET balance = ? WHERE id = ?";
    PreparedStatement updateStmt = conn.prepareStatement(updateSql);
    updateStmt.setInt(1, balance + 100);
    updateStmt.setLong(2, accountId);
    updateStmt.executeUpdate();
}
conn.commit();
conn.close();

Пессимистичная блокировка в Hibernate

Hibernate предоставляет удобные методы для пессимистичной блокировки:

// Режимы блокировки
LockModeType.PESSIMISTIC_READ   // Блокировка для чтения
LockModeType.PESSIMISTIC_WRITE  // Блокировка для записи
LockModeType.PESSIMISTIC_FORCE_INCREMENT  // Блокировка с инкрементом версии

// Получение сущности с блокировкой при поиске
Account account = entityManager.find(
    Account.class,
    accountId,
    LockModeType.PESSIMISTIC_WRITE
);
account.setBalance(account.getBalance() + 100);
// Блокировка освобождается при коммите

// Получение с блокировкой через JPQL
Query query = entityManager.createQuery(
    "SELECT a FROM Account a WHERE a.id = :id"
);
query.setParameter("id", accountId);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
Account account = (Account) query.getSingleResult();

Типы пессимистичной блокировки

1. PESSIMISTIC_READ (Shared Lock)

Частая блокировка для чтения. Несколько транзакций могут одновременно читать одни и те же данные, но ни одна не может писать:

entityManager.find(
    Product.class,
    productId,
    LockModeType.PESSIMISTIC_READ
);

2. PESSIMISTIC_WRITE (Exclusive Lock)

Исключительная блокировка. Только одна транзакция может получить доступ (читать или писать). Остальные ждут:

entityManager.find(
    Account.class,
    accountId,
    LockModeType.PESSIMISTIC_WRITE
);

3. PESSIMISTIC_FORCE_INCREMENT

Как PESSIMISTIC_WRITE, но автоматически инкрементирует поле версии:

@Entity
public class Account {
    @Id
    private Long id;
    
    @Version
    private Integer version;
    
    private BigDecimal balance;
}

// При блокировке версия автоматически увеличится
Account account = entityManager.find(
    Account.class,
    accountId,
    LockModeType.PESSIMISTIC_FORCE_INCREMENT
);

Timeout блокировки

Можно установить таймаут для избежания бесконечного ожидания:

Map<String, Object> properties = new HashMap<>();
properties.put("javax.persistence.lock.timeout", 3000); // 3 секунды

Account account = entityManager.find(
    Account.class,
    accountId,
    LockModeType.PESSIMISTIC_WRITE,
    properties
);

Пример использования: перевод денег

@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
    // Блокируем оба счёта для исключения race condition
    Account fromAccount = entityManager.find(
        Account.class,
        fromId,
        LockModeType.PESSIMISTIC_WRITE
    );
    
    Account toAccount = entityManager.find(
        Account.class,
        toId,
        LockModeType.PESSIMISTIC_WRITE
    );
    
    // Проверяем баланс
    if (fromAccount.getBalance().compareTo(amount) < 0) {
        throw new InsufficientFundsException();
    }
    
    // Проводим операцию
    fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
    toAccount.setBalance(toAccount.getBalance().add(amount));
}

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

ПараметрПессимистичнаяОптимистичная
БлокировкаДо начала работыПри сохранении
КонфликтыПредотвращаютсяОбнаруживаются
ПроизводительностьХорошо при частых конфликтахХорошо при редких конфликтах
DeadlocksВозможныНевозможны
OverheadВысокий при редких конфликтахНизкий

Когда использовать пессимистичную блокировку

  • Высокая конкурентность с частыми конфликтами (критичные операции)
  • Финансовые операции (переводы денег, инвентарь)
  • Критичные данные, где конфликты недопустимы
  • Небольшое количество одновременных пользователей

Проблемы пессимистичной блокировки

  • Deadlocks — циклические зависимости блокировок
  • Снижение производительности — транзакции ждут друг друга
  • Масштабируемость — при росте нагрузки возникают проблемы

Пессимистичная блокировка — это консервативный подход, гарантирующий безопасность данных, но требующий тщательного проектирования для избежания deadlocks и снижения производительности.