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

Что произойдет с полем quantity в таблице products, если два пользователя одновременно купят один и тот же товар

1️⃣ Как кратко ответить

Если два пользователя одновременно купят один и тот же товар, и система не использует механизмы синхронизации или блокировки, то может произойти состояние гонки (race condition), что приведет к некорректному обновлению поля quantity в таблице products. Это может привести к тому, что количество товара будет уменьшено неправильно, например, дважды уменьшено на одну и ту же единицу.

2️⃣ Подробное объяснение темы

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

Пример проблемы

Предположим, у нас есть таблица products с полем quantity, которое хранит количество доступных единиц товара. Два пользователя одновременно покупают одну единицу товара. Процесс покупки может выглядеть следующим образом:

  1. Пользователь A читает текущее значение quantity (например, 10).
  2. Пользователь B также читает текущее значение quantity (10).
  3. Пользователь A уменьшает quantity на 1 и записывает новое значение (9).
  4. Пользователь B уменьшает quantity на 1 и записывает новое значение (9).

В результате, хотя оба пользователя купили по одной единице, quantity уменьшилось только на 1 вместо 2, что является некорректным.

Решение проблемы

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

1. Транзакции и блокировки

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

BEGIN;
​
-- Блокируем строку, чтобы другие транзакции не могли её изменить
SELECT quantity FROM products WHERE product_id = ? FOR UPDATE;
​
-- Обновляем количество
UPDATE products SET quantity = quantity - 1 WHERE product_id = ?;
​
COMMIT;
  • BEGIN; — начало транзакции.
  • SELECT ... FOR UPDATE; — блокирует строку, чтобы другие транзакции не могли её изменить до завершения текущей транзакции.
  • UPDATE ...; — уменьшает количество товара.
  • COMMIT; — завершает транзакцию, снимая блокировку.

2. Оптимистическая блокировка

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

-- Предположим, что у нас есть поле version для отслеживания изменений
SELECT quantity, version FROM products WHERE product_id = ?;
​
-- При обновлении проверяем, что версия не изменилась
UPDATE products
SET quantity = quantity - 1, version = version + 1
WHERE product_id = ? AND version = ?;
  • SELECT ...; — читает текущее количество и версию.
  • UPDATE ... WHERE ... AND version = ?; — обновляет количество и версию, только если версия не изменилась.

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

Заключение

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

Тема: БД и транзакции
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки