Покинул форум
Сообщений всего: 9
Дата рег-ции: Май 2012
Помог: 0 раз(а)
Есть интернет-магазин, в базе данных есть таблица products(id,name,details,qty).
Есть форма заполнения заявки, где указывается количество товара.
Написал перед уменьшением количества товара и добавления заказа, стандартную проверку на php, проверяющую, что количество требуемого товара, не превышает количество товара на складе. И всё вроде работает.
До тех пор пока пользователи в интернете нажмут на кнопку заказать одновременно( вероятность мала, но есть). Тогда количество товара товара после 1-го заказа ещё не успеет уменьшится, и 2-ой заказ так же пройдёт проверку, и получится что пользователи закажут больше товара чем есть на складе.
Для решения этой проблемы посоветовали использовать блокировки. Вопрос в том какие блокировки использовать?
Как я понимаю всю таблицу блокировать не стоит, достаточно использовать блокировку отдельной записи, с которой работаем.
С помощью какой команды это делается? нашёл SELECT ... FOR UPDATE, но она видимо не совсем подходит.
DeepVarvar
Отправлено: 16 Мая, 2012 - 18:49:21
Активный участник
Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008 Откуда: Альфа Центавра
Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011 Откуда: Харьков
Помог: 126 раз(а)
Цитата:
Для решения этой проблемы посоветовали использовать блокировки. Вопрос в том какие блокировки использовать?
глупый совет
никакими блокировками и транзакциями это не делается
и твою проблему не решает никаким каком
единственный способ - резервировать товар (добавть поле в таблицу или специальную таблицу чтобы к юзеру привязать) и возвращать количество без резервированных. В случает незавершения покупки - резевирование отменяется
Цитата:
получится что пользователи закажут больше товара чем есть на складе.
они что заказывают весть товар что видят? или заказывают сколько надо? какое дело юзеру сколько там на складе
проблема на самом деле может быть только если осталось несколько штук. То есть она еще более маловероятна если следить за состоянием склада
Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008 Откуда: Альфа Центавра
Помог: 353 раз(а)
caballero пишет:
никакими блокировками и транзакциями это не делается
Я скинул к тому что атомарные операции апдейта и инсерта, как написано, лочат все сами, и тот кто читает из таблицы становится в очередь и ожидает, пока таблица разлочится.
Однако было дело, ставил эксперимент. Создал таблицу на 10 записей (id, name) id не уникальный, не автоинкремент, просто INT.
Добавляя в конец удалял первую запись.
Писатель:
Так вот при больших скоростях в цикле или при большом кол-ве "писателей", "читатель" получает не верные данные, дублирование id, больше 10 записей и прочие неинтересности...
Это связано и с кешем мускуля и с тем что в эксперименте у писателя не одна операция, а две.
Лечение: делать все одним запросом.
caballero пишет:
глупый совет
А вот вопрос в принципе верный. Или вы считаете что синхронизация данных не важна?
Покинул форум
Сообщений всего: 9
Дата рег-ции: Май 2012
Помог: 0 раз(а)
caballero пишет:
Лечение: делать все одним запросом.
Так даже если сделать всё одним запросом, то они оба сначала пройдут проверку, что товара достаточно, и затем каждый начнёт выполнять свой запрос, при этом один подождёт своей очереди, и всё равно сможет заказать, то чего нет. Разве нет?
вот та самая проверка и работа с таблицами
die("К сожалению на складе не достаточно товара.<br>");
}else
{
$query="INSERT INTO orders (date,product_id,product_qty,customer_id) VALUES('$date', $prod_id, $qty, $user_id)";
$qresult=queryMysql($query);
$query="UPDATE products SET qty=qty-$qty WHERE `id`=$prod_id";
$qresult=queryMysql($query);
}
где qty-количество товара (Добавление)
DeepVarvar пишет:
атомарные операции апдейта и инсерта, как написано, лочат все сами, и тот кто читает из таблицы становится в очередь и ожидает, пока таблица разлочится
Даже если тип таблиц у меня InnoDB?
Тоесть если два пользователя одновременно подойдут к строке UPDATE то один из них всё равно будет ждать?
Получается если изменить UPDATE запрос таким образом:
Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011 Откуда: Харьков
Помог: 126 раз(а)
Цитата:
Я скинул к тому что атомарные операции апдейта и инсерта, как написано, лочат все сами, и тот кто читает из таблицы становится в очередь и ожидает, пока таблица разлочится.
не подходит - ему надо залочить пока пока он не поменял значение. А нет изменений - нет транзакций.
Цитата:
А вот вопрос в принципе верный. Или вы считаете что синхронизация данных не важна?
синхронизация там ни при чем
Цитата:
То один из них изменит количество, у второго не сработает WHERE т.к. первый изменил поле qty и теперь оно не равно $ostatok. Так?
здесь нет никаких причин чтобы не сработал update
я вообще не вижу пробьлеммы
у тебя на складе 100 штук
один купил 2 другой 3
какая разница юзеру от чего отнимется эти 3 от 100 или от 98? какое ему вообще дело до того сколько на складе.
единственная проблемма если на складе осталось меньше 5.
UPDATE products SET qty=qty-$qty WHERE`id`=$prod_id AND`qty`=$ostatok
У первого Where, сработает т.к. поле qty=100, а второй дождавшись своей очереди, у него WHERE уже не сработает, т.к. qty=10 после изменений 1-го. а нужно чтобы было 100, разве это не причина чтобы не сработал UPDATE?
Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008 Откуда: Альфа Центавра
Помог: 353 раз(а)
mag1K я сейчас не особо вчитывался, но мысли у вас верные.
Есть кстати мысль лочить не базу, а файл с помощью php, там какраз будут ждать все.
Т.е. пока не отработал один с проверками и прочим, другие даже проверять не начнут.
Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008 Откуда: Альфа Центавра
Помог: 353 раз(а)
caballero пишет:
при таких заказах на складе должно быть несколько тысяч
Абсолютно согласен - это уже проблемы кладовщика.
Однако, mag1K, если отбросить склад и кладовщика, с программной точки зрения вы правильно мыслите - синхронизация важна.
caballero, хайлоад делали когда-нибудь?
Когда доступ к данным получают одновременно тысячи уников.
Если не делали, вспомните эту тему, когда начнете.
А если уже участвовали в таком проекте - не понятна ваша реакция на вопрос ТС и на ответы людей.
Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009 Откуда: Россия, Санкт-Петербург
Помог: 618 раз(а)
Отдельно замечу, что ключевое слово "транзакция" (в контексте begin, commit, rollback, а не атомарной операции СУБД) здесь не главное и проблему первоначального кода не решит, а только запутает.
Дело в том, что update будет ждать окончания транзакции. А select-то - нет.
Первая транзакция сделает селект, получит, например, 1, потом заапдейтит -1, должно остаться 0. Но если до момента commit'а первой стартует вторая транзакция - селект вернёт опять же 1. И после апдейта (вот который таки начнёт ждать окончание первой транзакции) и коммита второй транзакции значение в таблице будет отрицательным.
А решением коллизии в этом случае окажется уже всплывший в начале SELECT ... FOR UPDATE, который залочит строку на время этой транзакции. А если лок не прошёл - будет ждать, пока не сможет его захватить.
----- PostgreSQL DBA
caballero
Отправлено: 17 Мая, 2012 - 10:48:00
Активный участник
Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011 Откуда: Харьков
Помог: 126 раз(а)
Цитата:
А если уже участвовали в таком проекте - не понятна ваша реакция на вопрос ТС и на ответы людей.
потому что вопрос бессмысленный и соответственно ответы на него тоже.
Еще раз - если человек просто выбрал количество товаров но не изменил ничего в БД никакие транзакции или блокировки работат не будут.
Нет изменений - нет проблем с консистентностью (это то что ты называешь почему то синхронизацией) данных.
Если резервировать товар (в смысле количество товара в той же строке) то транзакции все равно не подходят - длинные транзакции (пока там юзер сформирует заказ) от нескольких юзеров заблокируют БД или еще хуже - приведут к дедлоку. Да и вряд ли получится транзакцию растянуть на несколько обращений к серверу - конект то каждый раз новый.
Еще раз - если товара достаточно ничего делать не нужно вообще. Если нет - все равно заказ выполнен не будет. Проблемма высосана их пальца. Тем более это интернет - у юзера скорее заглючит браузер или связь чем он наткнется на маловероятную ситуацию что кто то в тот же момент захочет купить такое количество что суммарное количество превысит то что на складе.
Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008 Откуда: Альфа Центавра
Помог: 353 раз(а)
caballero пишет:
Да и вряд ли получится транзакцию растянуть на несколько обращений к серверу - конект то каждый раз новый
На примере файла - коннектов/запросов/транзакций можно делать несколько, и лочить файл не на весь процесс формирования заказа, а только на момент апдейта данных в БД - это доли секунды.
Все гости форума могут просматривать этот раздел. Только зарегистрированные пользователи могут создавать новые темы в этом разделе. Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.