2010-02-23 2 views
0

Для игры браузера, у меня есть таблица базы данных бои:PHP/MySQL: Предотвращение повторяющихся действий быстро подряд

  • fightID
  • fromID
  • toID
  • моделировали (1 = истина , 0 = false)

Всякий раз, когда пользователь запрашивает страницу, I выбирает al л бои, которые еще должны быть смоделированы:

SELECT fightID, fromID, toID FROM fights WHERE simulated = 0 

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

UPDATE fights SET simulated = 1 WHERE fightID = X 
UPDATE users SET points = points+1 WHERE userID = WINNER 

проблема:

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

Как я могу избежать этой проблемы? Большое спасибо!

ответ

3

Если вы уверены, что как только вы прочтете бои из БД, они будут имитированы (т. Е. Не возможно, что пользователь уйдет и что некоторые бои оставлены без имитации), вы используете a transaction для чтения и обновления борется с одной, атомной операцией.

START TRANSACTION; 
SELECT fightID, fromID, toID FROM fights WHERE simulated = 0 
UPDATE fights SET simulated = 1 WHERE fightID = X 
COMMIT; 

, а затем обновить результаты.

+0

+1 для транзакций, для чего они предназначены. Но я не понимаю вашего оговорки об уходе пользователя; для чего нужны откаты. –

+1

Я знаю, что такое откат;). Но предположим, что исход боя вычисляется на стороне сервера, а затем отображается на стороне клиента с использованием флэш-анимации. Возможно, что пользователь не наблюдает за всеми боями, что может означать, что невидимый бой отменен. Я не вижу, как можно отменить часть транзакции в другом запросе (я также не вижу, как вы можете обнаружить, что пользователь уходит и отправляет этот последний запрос, если на то пошло). – Wookai

+0

Большое спасибо! Вы могли бы избежать проблемы с уходом пользователя, просто используя ignore_user_abort (true), не так ли? – caw

0

Вместо keepeng точек в таблице пользователей вы можете создать таблицу 'победителей

userId 
fightId 
... 

и (идентификатор пользователя, fightId) будет первичным ключом. Это помешает любому пользователю получить более 1 (или что-то еще) очков за бой. Точки будут вычисляться на select count(*) from winners where userId=?

+0

Строго говоря, это денормализует его БД, не так ли?)? – Wookai

+0

Я на 99,99% уверен, что это не будет, но я не помню всех определений уровней нормализации (только первые три, но это определенно не идет против них), поэтому, если бы вы могли указать недостаток я был бы признателен. – pablochan

+0

Я не был уверен, поэтому решил, что буду дразнить вас;) ... Я не знаю, противоречит ли это правилам, но кажется субоптимальным, поскольку у вас есть соотношение 1: 1 между таблицей победителей и стол битв. Таким образом, вам понадобится еще одно соединение, сохранение еще двух идентификаторов и т. Д. Опять же, это просто чувство и не основано на определенном правиле. – Wookai

-1

Хм, что, если вы используете 3 состояния моделирования? 0 = не моделируется, 1 = имитирует СЕЙЧАС, 2 = моделируется. Затем вы можете отмечать симуляцию сразу после того, как пользователь загружает бой.

+0

Это не решит проблему. Это именно то, что я делаю в данный момент. Если два пользователя получают доступ к странице почти одновременно, сценарий выбирает данные из MySQL в обоих случаях. Но если скрипт обновляет статус до 1 (имитирует NOW) для первого пользователя, второй пользователь уже выбрал данные ... – caw

Смежные вопросы