PHP и параллелизм - PullRequest
       10

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

7 голосов
/ 11 ноября 2009

Недавно я занимался веб-разработкой на 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 предлагает функцию LOCK table . Однако я не уверен, какой из них, если таковой используется, используется на практике.

Ответы [ 7 ]

11 голосов
/ 11 ноября 2009

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

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

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

5 голосов
/ 11 ноября 2009

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

4 голосов
/ 11 ноября 2009

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

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

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

2 голосов
/ 11 ноября 2009

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

1 голос
/ 20 ноября 2011

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

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

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

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

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

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

1 голос
/ 11 ноября 2009

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

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

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

0 голосов
/ 06 апреля 2014

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...