我正在制作一个多人游戏,它有一个大厅般的区域,玩家选择“扇区”进入。大厅网关由PHP提供支持,而实际的游戏玩法由一个或多个Java服务器处理。数据存储区是MySQL。
快乐的道路: 玩家选择一个扇区并告诉大厅他想要进入。 大厅检查这是否合适,包括检查扇区中是否有太多玩家(将该扇区的扇区分配中的条目数与扇区的max_players值进行比较)。 播放器被添加到sector_assignments表中,将其与扇区配对。玩家客户端收到一个密钥,让他连接到相应的游戏服务器。
竞争条件: 如果两个玩家在接近同一时间请求访问同一个扇区,我可以设想一个案例,它们都被添加,因为当他们的支票开始并且超过最大玩家时有一个空闲空间。
对于sector_assignments,最好的解决方案是LOCK TABLE吗?还有其他选择吗?
答案 0 :(得分:6)
通常,此类并发问题的解决方案涉及事务和乐观锁定:更新计数器时,添加where
子句以检查旧值并计算更新的行数
v = select value from counter where id=x.
update counter set value = v+1 where value = v and id=x
如果计数器在此期间更新,则更新不会更改任何行 - 因此您知道必须回滚并再次尝试交易。
一个问题是,它可能导致高争用,只有少数事务成功,而很多事情都失败了。
然后最好坚持悲观锁定,先锁定行,然后再更新它。但只有一个基准测试会告诉你。
修改强>
如果使用没有乐观锁定的事务,则可能发生以下情况。
Max authorized = 50. Current value = 49.
T1: start tx, read value --> 49
T2: start tx, read value --> 49
T1: update value --> 50, acquire a row lock
T1: commits --> release the lock
T2: update value --> 50, acquire a row lock
T2: commits --> release the lock
两个事务都成功,值为50,但存在不一致。
答案 1 :(得分:2)
如果您使用INNODB作为存储引擎,则可以在数据库中使用transactions,并且无需手动锁定表。
在单个交易中,检查空间是否可用,并将播放器添加到扇区。这可以保证在您提交事务之前,检查查询的结果仍然有效。