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

Как называется lock для сравнения сохраненного значения с сохраняемым в базу данных?

3.0 Senior🔥 61 комментариев
#Базы данных и SQL

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

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;

Оптимистическая блокировка исходит из предположения, что конфликты редки. Она не блокирует запись при чтении, а проверяет при обновлении, не изменились ли данные с момента чтения. Это реализуется через:

  1. Версионирование (versioning) - добавление числового поля version
  2. Метку времени (timestamp) - сравнение временных меток изменения
  3. Контрольную сумму (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;
    }
}

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

  1. Чтение данных с текущей версией:

    $product = $pdo->query("SELECT id, name, stock, version FROM products WHERE id = 123")->fetch();
    $currentVersion = $product['version'];
    
  2. Модификация данных в приложении:

    $product['stock'] -= 5;
    
  3. Попытка сохранения с проверкой версии:

    try {
        $repository->updateWithOptimisticLock(123, $product, $currentVersion);
        echo "Данные успешно сохранены";
    } catch (OptimisticLockException $e) {
        // Конфликт: данные были изменены кем-то другим
        echo "Ошибка: " . $e->getMessage();
        // Предложить пользователю обновить данные
    }
    

Преимущества и недостатки

Преимущества оптимистической блокировки:

  • Высокая производительность при редких конфликтах
  • Нет блокировок на уровне БД
  • Подходит для веб-приложений с короткими транзакциями
  • Меньше вероятность взаимоблокировок (deadlocks)

Недостатки:

  • Требует обработки исключений при конфликтах
  • Не подходит для частых конфликтов
  • Дополнительные поля в таблицах

Когда использовать:

  • Веб-приложения с высокой конкурентностью чтения
  • Системы, где пользователи редко редактируют одни и те же данные
  • REST API, где нельзя удерживать соединение долго

Когда НЕ использовать:

  • Финансовые системы с частыми изменениями баланса
  • Системы бронирования мест/билетов
  • Приложения с длительными сессиями редактирования

Практические рекомендации для PHP-разработчиков

  1. В Doctrine ORM есть встроенная поддержка через аннотацию @Version:

    /**
     * @Entity
     */
    class Product
    {
        /**
         * @Id @GeneratedValue @Column(type="integer")
         */
        private $id;
        
        /**
         * @Column(type="integer")
         * @Version
         */
        private $version;
        
        // ... остальные поля
    }
    
  2. Для REST API можно возвращать версию в ETag:

    // GET запрос
    $version = $product->getVersion();
    header("ETag: \"{$version}\"");
    
    // PUT запрос с проверкой
    if ($_SERVER['HTTP_IF_MATCH'] !== "\"{$version}\"") {
        http_response_code(412); // Precondition Failed
    }
    
  3. Альтернативный подход - сравнение всех значимых полей:

    UPDATE products 
    SET stock = 95 
    WHERE id = 123 
      AND stock = 100  -- Проверяем старое значение
      AND name = 'Товар A';
    

Оптимистическая блокировка - это важный паттерн для обеспечения целостности данных в высоконагруженных приложениях, позволяющий балансировать между производительностью и консистентностью. Выбор между оптимистическим и пессимистическим подходом зависит от конкретных требований приложения к конкурентности и частоте конфликтов.