我正在创建一个游戏,其中玩家可以挑战存储在数据库中的随机玩家。这就是我这样做的方式:( MySQL数据库)
当用户搜索随机用户时,如果没有用户等待,我将用户的'waiting'属性设置为1.
如果已经有用户在等待挑战(“等待”已经设置为1),我将它们匹配在一起并将其“等待”属性设置为0.(基本上,我在数据库表中查询任何用户'waiting'属性设置为1。)
我想我的第一个问题是,这是一个好主意吗?我关心的是同步。如果两个用户同时查询等待的玩家,那么等待的用户将被挑战两次。虽然选择查询和更新查询之间的时间范围非常小,但这在技术上是可行的,对吗?
有没有办法避免这种情况?或者这是我不应该担心的事情?
答案 0 :(得分:1)
你想要做的是创造某种互斥情况。基本上,您需要让查询增加一些数字,然后测试它是否是正确的值。
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
答案 1 :(得分:1)
如果您在找到两个想要匹配的玩家的同时更新“等待”标志,则任何其他查询都会看到该更新。 (这可能应该在存储过程中完成,以减少您正在搜索等待播放器的查询之间的时间,然后更新它们的状态。)如果两个查询尝试并在完全改变同一行同时,其中一个将失败,因为该行应该被锁定以进行更新。
我不确定MySQL是否会产生死锁异常。
确保您的客户端应用中有很好的异常处理。如果从此操作中获得异常指示更新失败,请再试一次。您可能希望生成一个有意引起您担心的情况的测试,并验证您的修复是否按预期工作。
我期望这种情况发生的唯一方法是,如果您使用参数化查询并且必须执行单独的查询以查找匹配,然后更新“等待”标志。使用存储过程,在服务器上一次完成所有操作,并享受更高的稳定性和安宁。
答案 2 :(得分:1)
你对并发部分是正确的。两个人可以挑战同一个用户并直到挑战部分。
一种可能的方法是锁定行以进行更新,同时运行的另一个会话将无法锁定相同的用户ID。
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
相反,您可以尝试第三个表,其中包含两个用户的ID,如下所示。?
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);
主键会阻止已经参与challlenge的用户再次添加到另一个用户。