PHP и параллелизм


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

Хотя я знаю базовый SQL и работал с базовыми манипуляциями с данными в базе данных, я не понимаю, как веб-разработчики, которые основывают свои веб-сайты на PHP/Javascript/SQL, могут управлять пользователями, изменяющими одни и те же данные в то же время.

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

Итак, допустим, псевдокод для функции, которая делает это, выглядит примерно так:

... 
$total = mysql_query("SELECT score FROM team1");
$total = $total + $mytotal;
mysql_query("UPDATE team1 SET score='".$total."'");
...

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

Мой вопрос в том, как этого избежать? Делается ли это с использованием PHP на уровне кода или существуют функции, предоставляемые любой используемой вами базой данных, которые помогают предотвратить это?

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

Author: Zachary Wright, 2009-11-11

7 answers

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

 5
Author: Byron Whitlock, 2009-11-11 03:55:11

Сохраняйте логику в БД, эта семантика в основном решена за вас.

update teams
set score = score + 1
where team_id = 1

..или любой счет и любой номер команды.

 11
Author: Jé Queue, 2009-11-11 03:45:08

Основные базы данных предназначены для обработки таких ситуаций.

При использовании MySQL механизм хранения InnoDB имеет функцию блокировки строк, которая блокирует строку, в которую записывается. Таким образом, любой запрос на обновление для одной и той же строки будет ждать, пока не завершится предыдущая операция над строкой.

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

 4
Author: Nirmal, 2009-11-11 03:56:16

Я согласен с ответом Xepochs, не вводите проблемы параллелизма, если вам это абсолютно необходимо. Для получения отличного ответа относительно различных стратегий блокировки см. здесь.

 2
Author: deceze, 2017-05-23 11:53:16

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

В большинстве случаев изменения происходят достаточно быстро, чтобы конфликт блокировок был минимальным. Но это все еще может произойти, поэтому вам нужно проверить состояние ошибки, возвращаемое после выполнения SQL-запроса. Функция mysql_query() возвращает FALSE, если есть проблема.

Затем вы должны выяснить причину (конфликт обновлений, недостаточные разрешения, синтаксическая ошибка SQL и т.д.), вызвав mysql_errno(). См. http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html ссылка на код ошибки MySQL.

 1
Author: Bill Karwin, 2009-11-11 03:54:51

Я не могу поверить, что ни один из приведенных выше ответов не указывает на то, что вы, возможно, столкнулись с недостатком дизайна.

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

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

    INSERT INTO team1(userid, amount) VALUES(32, 100);
    SELECT SUM(amount) FROM team1
or
    REPLACE INTO team1 SET amount = $total

Но если бы это был я, я бы не стал даже беспокоить. Я бы просто установил_cookie (team_score, team_score+сумма) и сохранил все на стороне клиента. Когда я хотел бы прочитать результат, я бы отправил запрос AJAX на чтение или запись на сервер и все равно обновлял клиентский интерфейс асинхронно. Поскольку вы указываете, что ваша веб-разработка является базовой, я рекомендую вам взглянуть на платформу jQuery для асинхронного взаимодействия клиент-сервер, поскольку это, безусловно, самый простой способ добиться этого.

РЕДАКТИРОВАТЬ: И для проблема обновления пользовательского интерфейса каждого пользователя, когда другой игрок добавляет сумму к счету, я предлагаю вам изучить backbone.js что хорошо согласует вашу модель данных с клиентом игроков, чтобы они получали немедленные обновления при изменении счета.

 1
Author: snez, 2011-12-15 06:42:55

Базы данных имеют встроенный контроль параллелизма, например, вы можете прочитать о контроле параллелизма postgres здесь. Если я прав, настройки pgsql по умолчанию - оптимистичная блокировка и изоляция зафиксированных транзакций, что означает, что каждая транзакция осведомлена об изменениях, внесенных другими незафиксированными транзакциями.

В вашем случае эти настройки в порядке, поэтому вы можете сделать просто UPDATE team1 SET score = score + mytotal, и база данных будет хорошо справляться с проблемами параллелизма.

Если у вас есть более длительная проблема с обновлением выбора, затем вы можете заблокировать строки вручную с помощью первого выбора с помощью запроса SELECT FOR UPDATE.

Это лучшие практики в настоящее время...

 0
Author: inf3rno, 2014-04-06 17:30:28