Как называется lock для сравнения сохраненного значения с сохраняемым в базу данных?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Optimistic Lock vs Pessimistic Lock: Контроль конкурентного доступа в БД
Для сравнения сохраненного значения с сохраняемым в базу данных при конкурентном доступе используется механизм оптимистической блокировки (Optimistic Lock). Это один из двух основных подходов к управлению параллельными транзакциями, противоположный пессимистической блокировке (Pessimistic Lock).
Разница между Optimistic и Pessimistic Lock
Пессимистическая блокировка предполагает, что конфликты происходят часто. Она блокирует запись на уровне базы данных при чтении, предотвращая изменения другими транзакциями. В SQL это достигается через SELECT ... FOR UPDATE:
-- Пессимистическая блокировка в MySQL
START TRANSACTION;
SELECT * FROM products WHERE id = 123 FOR UPDATE;
-- Запись заблокирована, другие транзакции ждут
UPDATE products SET stock = stock - 1 WHERE id = 123;
COMMIT;
Оптимистическая блокировка исходит из предположения, что конфликты редки. Она не блокирует запись при чтении, а проверяет при обновлении, не изменились ли данные с момента чтения. Это реализуется через:
- Версионирование (versioning) - добавление числового поля
version - Метку времени (timestamp) - сравнение временных меток изменения
- Контрольную сумму (checksum) - сравнение хэшей данных
Реализация оптимистической блокировки через версионирование
Наиболее распространенный подход - добавление поля version:
-- Структура таблицы с версией
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
stock INT,
version INT DEFAULT 0
);
Логика работы в приложении:
class ProductRepository
{
public function updateWithOptimisticLock(int $id, array $data, int $expectedVersion): bool
{
$pdo = $this->getConnection();
// Пытаемся обновить, только если версия не изменилась
$sql = "UPDATE products
SET name = :name, stock = :stock, version = version + 1
WHERE id = :id AND version = :expectedVersion";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':name' => $data['name'],
':stock' => $data['stock'],
':id' => $id,
':expectedVersion' => $expectedVersion
]);
// Если ни одна запись не обновлена - значит версия изменилась
if ($stmt->rowCount() === 0) {
throw new OptimisticLockException(
"Запись была изменена другим пользователем. Текущая версия изменилась."
);
}
return true;
}
}
Процесс работы оптимистической блокировки
-
Чтение данных с текущей версией:
$product = $pdo->query("SELECT id, name, stock, version FROM products WHERE id = 123")->fetch(); $currentVersion = $product['version']; -
Модификация данных в приложении:
$product['stock'] -= 5; -
Попытка сохранения с проверкой версии:
try { $repository->updateWithOptimisticLock(123, $product, $currentVersion); echo "Данные успешно сохранены"; } catch (OptimisticLockException $e) { // Конфликт: данные были изменены кем-то другим echo "Ошибка: " . $e->getMessage(); // Предложить пользователю обновить данные }
Преимущества и недостатки
Преимущества оптимистической блокировки:
- Высокая производительность при редких конфликтах
- Нет блокировок на уровне БД
- Подходит для веб-приложений с короткими транзакциями
- Меньше вероятность взаимоблокировок (deadlocks)
Недостатки:
- Требует обработки исключений при конфликтах
- Не подходит для частых конфликтов
- Дополнительные поля в таблицах
Когда использовать:
- Веб-приложения с высокой конкурентностью чтения
- Системы, где пользователи редко редактируют одни и те же данные
- REST API, где нельзя удерживать соединение долго
Когда НЕ использовать:
- Финансовые системы с частыми изменениями баланса
- Системы бронирования мест/билетов
- Приложения с длительными сессиями редактирования
Практические рекомендации для PHP-разработчиков
-
В Doctrine ORM есть встроенная поддержка через аннотацию
@Version:/** * @Entity */ class Product { /** * @Id @GeneratedValue @Column(type="integer") */ private $id; /** * @Column(type="integer") * @Version */ private $version; // ... остальные поля } -
Для REST API можно возвращать версию в ETag:
// GET запрос $version = $product->getVersion(); header("ETag: \"{$version}\""); // PUT запрос с проверкой if ($_SERVER['HTTP_IF_MATCH'] !== "\"{$version}\"") { http_response_code(412); // Precondition Failed } -
Альтернативный подход - сравнение всех значимых полей:
UPDATE products SET stock = 95 WHERE id = 123 AND stock = 100 -- Проверяем старое значение AND name = 'Товар A';
Оптимистическая блокировка - это важный паттерн для обеспечения целостности данных в высоконагруженных приложениях, позволяющий балансировать между производительностью и консистентностью. Выбор между оптимистическим и пессимистическим подходом зависит от конкретных требований приложения к конкурентности и частоте конфликтов.