如何在程序设计上减少不必要的并发冲突

有多个请求更新同一条数据库记录时,就可能出现冲突。

比如在农场游戏中,有一块地现在种植了20朵玫瑰。同一时间玩家A采集了2朵,玩家B采集3朵,玩家C采集了4朵。此时三个请求一起到达,对数据库的更新就很可能出现问题。

假设玩家A采集2朵时,$field 读取出来的 quantity 值是 20,则 save() 后,quantity 的新值是 18。如果在处理玩家A的请求时,程序执行到 save() 之前,服务器就开始处理玩家B采集3朵的请求,则玩家B读取到的 quantity 值将是玩家A更新前的值。两个请求谁最后调用save(),数据库中就是哪个请求写入的值。最终结果是两个玩家分别采集了 2 朵和 3 朵,但剩余数量却不是 15 朵。

要避免此问题,一种常见的解决办法就是使用数据库事务。
使用事务后,同一时间达到的请求,只有关闭一个事务后创建的新事务才能成功将数据写入数据库,这样就避免了数据不一致的问题。但是,使用数据库事务即便不考虑并发性能,也会明显影响玩家的游戏体验。

三个玩家同一时间采集,采集是否成功取决于谁的请求最先被服务器处理,只有三分之一的成功率。如果有更多玩家同一时间采集,那么成功率就更低了。

那么还能怎么改进sql能解决这个问题呢?

评论 (0)链接2012-02-11 

你说的这种问题,SQL语句:
UPDATE field SET quantity = quantity-采集数量 WHERE iPlayerID = "玩家ID"。
这样应该没有问题吧,事务也不用使用的。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (2)链接 • 2012-02-12
  • 0 支持
    这个解决不了并发问题,如果采集数量都是20呢?那岂不是相当于quantity-=60了? – 丛杰 2012-02-13
  • 0 支持
    这样不会发生你所说的那种情况,你要看到这里使用的是quantity而不是一个具体的数值,具体的解释可以看色色的答案,和我这里说的是一回事。 – 4GSpace 2012-02-22

改进更新数据库的方式

造成这种困扰的原因在于更新数据库的方式。save() 操作会生成如下的 SQL 查询:

UPDATE field
SET quantity = 18
WHERE player_id = 1
AND field_inx = 1

如果换个角度考虑,玩家对土地的采集,实际上是做了一个“减法”操作,所以更理想的 SQL 应该是:

UPDATE field
SET quantity = quantity - 2
WHERE player_id = 1
AND field_inx = 1

由于土地的剩余作物数量不可能是负数,所以 SQL 还要增加一个条件:

UPDATE field
SET quantity = quantity - 2
WHERE player_id = 1
AND field_inx = 1
AND quantity >= 2

当使用上面的 SQL 时,我们就会发现数据库事务纯粹多余了。三个玩家采集请求同时到达后,最后的结果就是 quantity = 11。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (3)链接 • 2012-02-13
  • 0 支持
    思路蛮好的(评论内容不得少于6个字) – xiaomi 2012-02-13
  • 0 支持
    学了一招,呵呵 – dropme 2013-01-28
  • 0 支持
    怎么是自问自答的 – wei3323 2013-03-08
德问是一个专业的编程问答社区,请 登录注册 后再提交答案