2011-04-19 45 views
2

我正在创建一个游戏,玩家可以挑战存储在数据库中的随机播放器。这就是我正在做的:(MySQL数据库)修改同一行的多个查询

当随机用户的用户搜索,如果没有用户的等待,我设置了用户的“等待”属性为1

如果有已经是等待挑战的用户('等待'已设置为1),我将它们匹配在一起并将其“等待”属性设置为0.(基本上,我查询数据库表的任何用户的“等待”属性设置为1.)

我想我的第一个问题是,这是一个好主意吗?我关心的是同步。如果两个用户同时查询等待的玩家,则等待的用户将被挑战两次。虽然选择查询和更新查询之间的时间范围非常小,但这在技术上是可行的,对吧?

有没有办法避免这种情况?或者是我不应该担心的事情?

+0

ISAM或InnoDB? – 2011-04-19 17:12:22

+0

我想你可以使用表锁。 – Brad 2011-04-19 17:13:24

+0

正确的话,如果你不在应用层处理它,你应该使用表锁。试试看http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html – aweis 2011-04-19 17:17:19

回答

1

如果您更新“等待”标志,同时发现两个要匹配的玩家,则任何其他查询都会看到该更新。 (这应该可以在存储过程中完成,以减少搜索等待播放器的查询之间的时间,然后更新它们的状态。)如果两个查询试图在处同时尝试并更改同一行,其中一个将失败,因为该行应该被锁定以进行更新。

我不确定MySQL是否会产生死锁异常。

确保您的客户端应用程序具有良好的异常处理。如果您从此操作中收到异常,表示更新失败,请重试。您可能希望生成一个测试,该测试将故意引起您担心的状况,并验证您的修复程序是否按预期工作。

我期望这种情况发生的唯一方法是如果您使用参数化查询并且必须执行单独的查询来查找匹配,然后更新“等待”标志。使用存储的proc,在服务器上一次完成,享受更高的稳定性和安宁性。

+0

我同意这一点。我可以避免同步问题,如果我先更新而不是尝试选择然后更新。谢谢。 – jnortey 2011-04-19 17:54:44

+0

理论上,两次会话是不是可以看到用户IDLE并将其更新为1(同时),从而使他面临两个挑战?除非您在更新之前为用户锁定行,否则始终存在此风险。 – 2011-04-19 20:59:29

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

你是对的并发部分。两个人可以挑战同一个用户,并完成挑战部分。

一种可能的方法是对锁定更新行,即另一个同时运行的会话将无法获取同一用户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用户再次添加到另一个。