代码之家  ›  专栏  ›  技术社区  ›  justkevin

插入行并避免竞争条件(php/mysql)

  •  3
  • justkevin  · 技术社区  · 15 年前

    我正在开发一款多人游戏,它有一个类似大厅的区域,玩家可以在这里选择要进入的“扇区”。大厅网关是由PHP供电的,而实际的游戏是由一个或多个Java服务器处理的。数据存储是mysql。

    幸福之路: 玩家选择一个扇区并告诉大厅他想进入。 大厅检查这是否正常,包括检查该扇区中是否有太多玩家(将该扇区的扇区分配中的条目计数与该扇区的最大玩家值进行比较)。 玩家被添加到扇区分配表中,与扇区配对。玩家客户端收到一个密码,让他连接到适当的游戏服务器。

    比赛条件: 如果两个玩家在接近同一时间请求访问同一个扇区,我可以设想这样一种情况,他们都会被添加,因为当他们开始检查时有一个空闲空间,并且超过了最大玩家。

    扇区分配的最佳解决方案锁定表是什么?还有别的选择吗?

    2 回复  |  直到 15 年前
        1
  •  6
  •   ewernli    15 年前

    通常,这种并发问题的解决方案涉及事务和 乐观锁定 :更新计数器时,添加 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,但不一致。

        2
  •  2
  •   grossvogel    15 年前

    如果使用innodb作为存储引擎,则可以使用 transactions 在数据库中,避免需要手动锁定表。

    在单个事务中,检查空间是否可用,并将播放机添加到扇区。这样可以保证检查查询的结果在提交事务之前仍然有效。