Несколько запросов, изменяющих одну и ту же строку - PullRequest
2 голосов
/ 19 апреля 2011

Я создаю игру, в которой игрок может бросить вызов случайному игроку, хранящемуся в базе данных. Вот как я это делаю: (база данных MySQL)

Когда пользователь ищет случайного пользователя, если нет ожидающих пользователей, я устанавливаю для атрибута ожидания пользователя значение 1.

Если пользователь уже ожидает вызова («ожидание» уже установлено в 1), я сопоставляю их вместе и устанавливаю их атрибуты ожидания в 0. (По сути, я запрашиваю таблицу базы данных для любых пользователей с Атрибут «Ожидание» установлен на 1.)

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

Есть ли способ избежать этого? Или это то, о чем мне не стоит беспокоиться?

Ответы [ 3 ]

1 голос
/ 19 апреля 2011

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

Я не уверен, что MySQL сгенерирует исключение взаимоблокировки или нет.

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

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

1 голос
/ 19 апреля 2011

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

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

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

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

create table users(
   id number,
   name varchar2(200)
);

create table challenge(
   id1 number,
   id2 number,
   constraint pk1 primary key (id1,id2),
   constraint fk1 fk_id1 references users(id1),
   constraint fk2 fk_id2 references users(id),
   );

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

select * from users where id not in (
 select id1 from challenge
   union
 select id2 from challenge);

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

1 голос
/ 19 апреля 2011

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

SELECT *
FROM players
WHERE ready = 1
AND connections = 0

, тогда вам нужно иметь приращение

UPDATE players
SET connections = connections + 1
WHERE id IN (...)

, тогда вам следует проверитьчто номер соединения все еще один (кто-то еще бросил вызов тому же игроку)

SELECT *
FROM players
WHERE connections = 1
AND id IN (...)

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

UPDATE players
SET connections = 0
WHERE connection > 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...