Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое пессимистичная транзакция?
Пессимистичная транзакция (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 и снижения производительности.