我在 Rails 模型中有以下代码:
foo = Food.find(...)
foo.with_lock do
if bar = foo.bars.find_by_stuff(stuff)
# do something with bar
else
bar = foo.bars.create!
# do something with bar
end
end
目标是确保正在创建的类型的 Bar 不会被创建两次。
在控制台测试 with_lock 的效果证实了我的预期。然而,在生产中,似乎在某些或所有情况下锁都没有按预期工作,并且正在尝试冗余 Bar —— 因此,with_lock 不会(总是?)导致代码等待轮到它.
这里会发生什么?
更新 对所有说“锁定 foo 不会帮助你”的人感到抱歉!我的示例最初没有栏查找。现在已修复。
最佳答案
您对 with_lock
的作用感到困惑。来自fine manual :
with_lock(lock = true)
Wraps the passed block in a transaction, locking the object before yielding. You pass can the SQL locking clause as argument (see
lock!
).
如果您检查一下 with_lock
内部做了什么,您会发现它只不过是一个围绕 lock!
的薄包装而已。 :
lock!(lock = true)
Obtain a row lock on this record. Reloads the record to obtain the requested lock.
所以 with_lock
只是进行行锁定并锁定 foo
的行。
不要为所有这些锁定废话而烦恼。处理这种情况的唯一明智的方法是在数据库中使用唯一性约束,只有数据库才能确保唯一性,除非您想做一些荒谬的事情,例如锁定整个表;然后继续并盲目地尝试您的 INSERT 或 UPDATE 并捕获并忽略在违反唯一约束时将引发的异常。
关于ruby-on-rails - 数据库锁在 Rails 和 Postgres 中无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11313303/