Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: Блокировки в MySQL

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (2): [1] 2 »   

> Без описания
mag1K
Отправлено: 16 Мая, 2012 - 18:40:20
Post Id



Новичок


Покинул форум
Сообщений всего: 9
Дата рег-ции: Май 2012  


Помог: 0 раз(а)




Есть интернет-магазин, в базе данных есть таблица products(id,name,details,qty).
Есть форма заполнения заявки, где указывается количество товара.
Написал перед уменьшением количества товара и добавления заказа, стандартную проверку на php, проверяющую, что количество требуемого товара, не превышает количество товара на складе. И всё вроде работает.
До тех пор пока пользователи в интернете нажмут на кнопку заказать одновременно( вероятность мала, но есть). Тогда количество товара товара после 1-го заказа ещё не успеет уменьшится, и 2-ой заказ так же пройдёт проверку, и получится что пользователи закажут больше товара чем есть на складе.

Для решения этой проблемы посоветовали использовать блокировки. Вопрос в том какие блокировки использовать?
Как я понимаю всю таблицу блокировать не стоит, достаточно использовать блокировку отдельной записи, с которой работаем.
С помощью какой команды это делается? нашёл SELECT ... FOR UPDATE, но она видимо не совсем подходит.
 
 Top
DeepVarvar Супермодератор
Отправлено: 16 Мая, 2012 - 18:49:21
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




 
 Top
caballero
Отправлено: 16 Мая, 2012 - 19:04:21
Post Id


Активный участник


Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011  
Откуда: Харьков


Помог: 126 раз(а)




Цитата:
Для решения этой проблемы посоветовали использовать блокировки. Вопрос в том какие блокировки использовать?

глупый совет
никакими блокировками и транзакциями это не делается
и твою проблему не решает никаким каком

единственный способ - резервировать товар (добавть поле в таблицу или специальную таблицу чтобы к юзеру привязать) и возвращать количество без резервированных. В случает незавершения покупки - резевирование отменяется

Цитата:
получится что пользователи закажут больше товара чем есть на складе.

они что заказывают весть товар что видят? или заказывают сколько надо? какое дело юзеру сколько там на складе
проблема на самом деле может быть только если осталось несколько штук. То есть она еще более маловероятна если следить за состоянием склада

(Отредактировано автором: 16 Мая, 2012 - 19:05:02)



-----
Бесплатная система складского учета с открытым кодом https://zippy[dot]com[dot]ua/zstore
 
 Top
DeepVarvar Супермодератор
Отправлено: 16 Мая, 2012 - 19:17:14
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




caballero пишет:
никакими блокировками и транзакциями это не делается
Я скинул к тому что атомарные операции апдейта и инсерта, как написано, лочат все сами, и тот кто читает из таблицы становится в очередь и ожидает, пока таблица разлочится.

Однако было дело, ставил эксперимент. Создал таблицу на 10 записей (id, name) id не уникальный, не автоинкремент, просто INT.
Добавляя в конец удалял первую запись.
Писатель:
CODE (SQL):
скопировать код в буфер обмена
  1. UPDATE tbl SET id = id -1; INSERT INTO tbl (id, name) VALUES (10, 'что-то там');

Читатель:

Так вот при больших скоростях в цикле или при большом кол-ве "писателей", "читатель" получает не верные данные, дублирование id, больше 10 записей и прочие неинтересности...

Это связано и с кешем мускуля и с тем что в эксперименте у писателя не одна операция, а две.

Лечение: делать все одним запросом.
caballero пишет:
глупый совет
А вот вопрос в принципе верный. Или вы считаете что синхронизация данных не важна?
 
 Top
mag1K
Отправлено: 16 Мая, 2012 - 19:37:58
Post Id



Новичок


Покинул форум
Сообщений всего: 9
Дата рег-ции: Май 2012  


Помог: 0 раз(а)




caballero пишет:
Лечение: делать все одним запросом.


Так даже если сделать всё одним запросом, то они оба сначала пройдут проверку, что товара достаточно, и затем каждый начнёт выполнять свой запрос, при этом один подождёт своей очереди, и всё равно сможет заказать, то чего нет. Разве нет?
вот та самая проверка и работа с таблицами
PHP:
скопировать код в буфер обмена
  1. $query = "SELECT qty FROM `products` WHERE id=$prod_id";
  2.         $qresult=queryMysql($query);
  3.         $res=mysql_fetch_assoc($qresult);
  4.         $ostatok=$res['qty'];
  5.         if($ostatok < $qty){
  6.             die("К сожалению на складе не достаточно товара.<br>");
  7.         } else
  8.           {
  9.             $query="INSERT INTO orders (date,product_id,product_qty,customer_id) VALUES('$date', $prod_id, $qty, $user_id)";
  10.             $qresult=queryMysql($query);
  11.    
  12.             $query="UPDATE products SET qty=qty-$qty WHERE `id`=$prod_id";
  13.             $qresult=queryMysql($query);
  14.           }


где qty-количество товара
(Добавление)
DeepVarvar пишет:
атомарные операции апдейта и инсерта, как написано, лочат все сами, и тот кто читает из таблицы становится в очередь и ожидает, пока таблица разлочится

Даже если тип таблиц у меня InnoDB?
Тоесть если два пользователя одновременно подойдут к строке UPDATE то один из них всё равно будет ждать?
Получается если изменить UPDATE запрос таким образом:
CODE (SQL):
скопировать код в буфер обмена
  1. UPDATE products SET qty=qty-$qty WHERE `id`=$prod_id AND `qty`=$ostatok

То один из них изменит количество, у второго не сработает WHERE т.к. первый изменил поле qty и теперь оно не равно $ostatok. Так?

(Отредактировано автором: 16 Мая, 2012 - 20:07:14)

 
 Top
caballero
Отправлено: 16 Мая, 2012 - 20:42:05
Post Id


Активный участник


Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011  
Откуда: Харьков


Помог: 126 раз(а)




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

не подходит - ему надо залочить пока пока он не поменял значение. А нет изменений - нет транзакций.

Цитата:
А вот вопрос в принципе верный. Или вы считаете что синхронизация данных не важна?

синхронизация там ни при чем

Цитата:
То один из них изменит количество, у второго не сработает WHERE т.к. первый изменил поле qty и теперь оно не равно $ostatok. Так?

здесь нет никаких причин чтобы не сработал update

я вообще не вижу пробьлеммы
у тебя на складе 100 штук
один купил 2 другой 3


какая разница юзеру от чего отнимется эти 3 от 100 или от 98? какое ему вообще дело до того сколько на складе.

единственная проблемма если на складе осталось меньше 5.

(Отредактировано автором: 16 Мая, 2012 - 20:43:25)



-----
Бесплатная система складского учета с открытым кодом https://zippy[dot]com[dot]ua/zstore
 
 Top
mag1K
Отправлено: 16 Мая, 2012 - 21:46:17
Post Id



Новичок


Покинул форум
Сообщений всего: 9
Дата рег-ции: Май 2012  


Помог: 0 раз(а)




caballero пишет:
я вообще не вижу пробьлеммы
у тебя на складе 100 штук
один купил 2 другой 3

проблемы возникнут если один закажет 90 и второй 90.

caballero пишет:
здесь нет никаких причин чтобы не сработал update

до проверка в php, мы вычислим что остаток=100,

потом они одновременно придут к запросу
CODE (SQL):
скопировать код в буфер обмена
  1. UPDATE products SET qty=qty-$qty WHERE `id`=$prod_id AND `qty`=$ostatok

У первого Where, сработает т.к. поле qty=100, а второй дождавшись своей очереди, у него WHERE уже не сработает, т.к. qty=10 после изменений 1-го. а нужно чтобы было 100, разве это не причина чтобы не сработал UPDATE?

(Отредактировано автором: 16 Мая, 2012 - 21:46:45)

 
 Top
DeepVarvar Супермодератор
Отправлено: 16 Мая, 2012 - 22:09:55
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




mag1K я сейчас не особо вчитывался, но мысли у вас верные.
Есть кстати мысль лочить не базу, а файл с помощью php, там какраз будут ждать все.
Т.е. пока не отработал один с проверками и прочим, другие даже проверять не начнут.
PHP:
скопировать код в буфер обмена
  1. $lockfile = @fopen("./data/lockfile", "w+b");
  2. if ($lockfile) {
  3.  
  4.   // лочим
  5.   flock($lockfile, LOCK_EX);
  6.  
  7.  
  8.   // TODO, делаем все что нужно, никто нам не помешает
  9.   // остальные должны так-же пытаться открывать этот файл
  10.   // и будут вставать в очередь
  11.  
  12.  
  13.   // разлочиваем и закрываем
  14.   flock($lockfile, LOCK_UN);
  15.   fclose($lockfile);
  16.  
  17. }

Данная блокировка файла 100% будет работать под *NIX, нащет винды не скажу...
А там вообще такие вещи есть? caballero, не подскажете?
 
 Top
caballero
Отправлено: 16 Мая, 2012 - 23:26:20
Post Id


Активный участник


Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011  
Откуда: Харьков


Помог: 126 раз(а)




Цитата:
проблемы возникнут если один закажет 90 и второй 90

при таких заказах на складе должно быть несколько тысяч

в любом случае не вижу проблеммы - если товара не хватит он по любому не сможет купить как ни перекладывай цифры в БД.

Цитата:
А там вообще такие вещи есть? caballero, не подскажете?

есть, но я такой фигней не страдаю и не представляю зачем логчить файл


-----
Бесплатная система складского учета с открытым кодом https://zippy[dot]com[dot]ua/zstore
 
 Top
DeepVarvar Супермодератор
Отправлено: 17 Мая, 2012 - 09:42:26
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




caballero пишет:
при таких заказах на складе должно быть несколько тысяч
Абсолютно согласен - это уже проблемы кладовщика.

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

caballero, хайлоад делали когда-нибудь?
Когда доступ к данным получают одновременно тысячи уников.
Если не делали, вспомните эту тему, когда начнете.
А если уже участвовали в таком проекте - не понятна ваша реакция на вопрос ТС и на ответы людей.
 
 Top
Мелкий Супермодератор
Отправлено: 17 Мая, 2012 - 09:58:17
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




Зачем вам блокировки, если всё, что вам нужно - не продать больше, чем есть?
CODE (SQL):
скопировать код в буфер обмена
  1. UPDATE products SET qty=qty-$qty WHERE `id`=$prod_id AND qty>=$qtu;

Если affected_rows > 0 - пишете в orders, товар успешно зарезервирован.
Если 0 - на складе недостаточно шушпанчиков.

Если дальше есть ещё условия, которые могут прервать заказ - оборачиваете всё в транзакцию и делаете rollback при сбое.

mag1K пишет:
Даже если тип таблиц у меня InnoDB?
Тоесть если два пользователя одновременно подойдут к строке UPDATE то один из них всё равно будет ждать?

Блокировка у innoDB построчная. Т.е. если 2 транзакции попытаются изменить 1 строку, вторая транзакция будет ждать окончания первой.


-----
PostgreSQL DBA
 
 Top
DeepVarvar Супермодератор
Отправлено: 17 Мая, 2012 - 09:59:33
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Мелкий пишет:
оборачиваете всё в транзакцию
В одну Закатив глазки
 
 Top
Мелкий Супермодератор
Отправлено: 17 Мая, 2012 - 10:47:42
Post Id



Активный участник


Покинул форум
Сообщений всего: 11926
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 618 раз(а)




Отдельно замечу, что ключевое слово "транзакция" (в контексте begin, commit, rollback, а не атомарной операции СУБД) здесь не главное и проблему первоначального кода не решит, а только запутает.
Дело в том, что update будет ждать окончания транзакции. А select-то - нет.
Первая транзакция сделает селект, получит, например, 1, потом заапдейтит -1, должно остаться 0. Но если до момента commit'а первой стартует вторая транзакция - селект вернёт опять же 1. И после апдейта (вот который таки начнёт ждать окончание первой транзакции) и коммита второй транзакции значение в таблице будет отрицательным.

А решением коллизии в этом случае окажется уже всплывший в начале SELECT ... FOR UPDATE, который залочит строку на время этой транзакции. А если лок не прошёл - будет ждать, пока не сможет его захватить.


-----
PostgreSQL DBA
 
 Top
caballero
Отправлено: 17 Мая, 2012 - 10:48:00
Post Id


Активный участник


Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011  
Откуда: Харьков


Помог: 126 раз(а)




Цитата:
А если уже участвовали в таком проекте - не понятна ваша реакция на вопрос ТС и на ответы людей.


потому что вопрос бессмысленный и соответственно ответы на него тоже.
Еще раз - если человек просто выбрал количество товаров но не изменил ничего в БД никакие транзакции или блокировки работат не будут.
Нет изменений - нет проблем с консистентностью (это то что ты называешь почему то синхронизацией) данных.

Если резервировать товар (в смысле количество товара в той же строке) то транзакции все равно не подходят - длинные транзакции (пока там юзер сформирует заказ) от нескольких юзеров заблокируют БД или еще хуже - приведут к дедлоку. Да и вряд ли получится транзакцию растянуть на несколько обращений к серверу - конект то каждый раз новый.

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


-----
Бесплатная система складского учета с открытым кодом https://zippy[dot]com[dot]ua/zstore
 
 Top
DeepVarvar Супермодератор
Отправлено: 17 Мая, 2012 - 11:26:56
Post Id



Активный участник


Покинул форум
Сообщений всего: 10377
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




caballero пишет:
Да и вряд ли получится транзакцию растянуть на несколько обращений к серверу - конект то каждый раз новый
На примере файла - коннектов/запросов/транзакций можно делать несколько, и лочить файл не на весь процесс формирования заказа, а только на момент апдейта данных в БД - это доли секунды.
 
 Top
Страниц (2): [1] 2 »
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« Работа с СУБД »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB