【问题标题】:How to handle concurrent requests that delete and create the same rows?如何处理删除和创建相同行的并发请求?
【发布时间】:2015-04-23 00:51:57
【问题描述】:

我有一个如下所示的表格:

game_stats 表:

id | game_id | player_id | stats | (many other cols...)
----------------------
1  | 'game_abc' | 8 | 'R R A B S' | ...
2  | 'game_abc' | 9 | 'S B A S' | ...

用户批量上传给定游戏的数据,同时提交两个玩家的数据。例如:

"game": {
  id: 'game_abc',
  player_stats: {
    8: {
      stats: 'R R A B S'
    },
    9: {
      stats: 'S B A S'
    }
  }
}

将此提交到我的服务器应该会生成第一个表。

当再次提交相同的数据(例如,带有修订)时,我不会更新现有行,而是首先删除game_stats 表中具有给定game_id 的所有现有行:

class GameStatController
    def update
      GameStat.where("game_id = ?", game_id).destroy_all
      params[:game][:player_stats].each do |stats|
        game_stat.save
      end
    end
end

这适用于单线程或单进程服务器。问题是我正在运行 Unicorn,它是一个多进程服务器。如果两个请求同时进入,我会得到一个竞争条件:

Request 1: GameStat.where(...).destroy_all
Request 2: GameStat.where(...).destroy_all
Request 1: Save new game_stats
Request 2: Save new game_stats

结果:具有相同数据的多个 game_stat 行。

我相信以某种方式锁定行或表是防止同时进行多个更新的方法 - 但我不知道该怎么做。与事务结合似乎是正确的做法,但我不太明白为什么。

编辑

澄清为什么我不知道如何使用锁定:我不能一次锁定一行,因为该行只是被删除而不是修改。

【问题讨论】:

    标签: ruby-on-rails-3 database-concurrency


    【解决方案1】:

    默认情况下,AR 不支持表级锁定。您必须执行特定于数据库的 SQL 或使用像 Monogamy 这样的 gem

    如果没有别的,将保存语句包装在事务中会加快速度。

    另一种选择是使用 Redis 实现锁。像redis-lock 这样的宝石也可用。这可能会降低风险,因为它不会触及数据库,并且您可以将 Redis 密钥设置为过期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-09
      • 1970-01-01
      • 2021-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-19
      相关资源
      最近更新 更多