you*_*786 5 database-concurrency ruby-on-rails-3
我有一个如下所示的表:
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' | ...
Run Code Online (Sandbox Code Playgroud)
用户批量上传给定游戏的数据,一次提交两个玩家的数据.例如:
"game": {
id: 'game_abc',
player_stats: {
8: {
stats: 'R R A B S'
},
9: {
stats: 'S B A S'
}
}
}
Run Code Online (Sandbox Code Playgroud)
将此提交到我的服务器应该会产生第一个表.
当我再次提交相同的数据时(例如,使用修订版),我在控制器中执行的操作首先删除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
Run Code Online (Sandbox Code Playgroud)
这适用于单线程或单进程服务器.问题是我正在运行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
Run Code Online (Sandbox Code Playgroud)
结果:具有相同数据的多个game_stat行.
我相信以某种方式锁定行或表是同时防止多个更新的方法 - 但我无法弄清楚如何做到这一点.结合交易似乎是正确的做法,但我不明白为什么.
编辑
澄清为什么我无法弄清楚如何使用锁定:我不能一次锁定一行,因为该行只是被删除而不是修改.
AR默认不支持表级锁定。你必须执行数据库特定的 SQL 或使用像Monogamy这样的 gem
如果不出意外的话,将保存语句包装在事务中将会加快速度。
另一种选择是使用 Redis 来实现锁。像redis-lock这样的 gem也是可用的。这可能风险较小,因为它不接触数据库,并且您可以将 Redis 密钥设置为过期。
| 归档时间: |
|
| 查看次数: |
499 次 |
| 最近记录: |