Rails如何在不锁定的情况下触碰Active Record对象?

14

在我的照片类中,我有这个关联。

belongs_to :user, :touch => true

有一天我收到了这个异常。

A ActiveRecord::StatementInvalid occurred in photos#update:

 Mysql::Error: Deadlock found when trying to get lock; try restarting transaction:
 UPDATE `users` SET `updated_at` = '2011-09-20 14:17:44' WHERE `users`.`id` = 6832
 production/ruby/1.8/gems/activerecord-3.0.10/lib/active_record/connection_adapters/abstract_adapter.rb:207:in `log'

我应该怎么做才能防止未来发生这样的异常?如果可能的话,我希望更新语句不使用锁定。我认为在这种情况下使用乐观锁定可能行不通,因为乐观锁定可能会引发ActiveRecord::StaleObjectError异常。

1个回答

10

我自己也遇到了这个问题。

简短回答:没有简单的方法解决这个问题。所有的touch都被包含在同一个事务中,因此死锁。

详细回答:我猜想你需要使用touch对象来使一些(依赖)缓存失效。通常推荐使用touch只适用于有限数量的"关系"。例如,在更新评论时使文章无效。

我的解决方案是使用异步收集(使用sidekiq作业)需要使其失效的DB对象集合。我编写了自己的控制逻辑,定义当对象更改时需要使哪些(其他)对象失效。例如,评论==>文章。

这样我们就有了一种更加详细的方法来失效依赖对象。此外,我使用了Model.update_all进行失效,比“触摸链”更快。它解决了我们的死锁问题,并为我们的缓存失效添加了详细信息和性能。

额外提示:不要使用updated_at。这引起了很大争议,因为一个DB对象是否真的因为另一个对象而改变。覆盖cache_key模型让您轻松定义自定义缓存键,如"#{id}-#{valid_from}"valid_from可以是您在模型上定义的时间戳(并且您使用它而不是updated_at)。


感谢您的回答。现在已经过去了2年,我并不完全确定为什么要使用touch。清除缓存似乎是一个很好的理由。我现在使用sweepers来过期缓存条目。http://guides.rubyonrails.org/caching_with_rails.html#sweepers。我再也不会在任何地方使用touch了。 - Eric Coulthard
我喜欢valid_from的想法,但我认为你还需要将类名添加到cache_key中,例如[class]/[id]-[timestamp],如http://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works所述。 - iheggie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接