Rails如何触摸Active Record Object而不锁定?

Eri*_*ard 11 caching locking ruby-on-rails associations touch

在我的照片课上,我有这种关联.

belongs_to :user, :touch => true
Run Code Online (Sandbox Code Playgroud)

有一天我得到了这个例外.

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'
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能防止此类事件发生?我希望错误中显示的更新语句尽可能不使用锁定.我不认为在这种情况下使用乐观锁定会起作用,因为乐观锁定可能会引发ActiveRecord :: StaleObjectError.

小智 8

这也是我偶然发现的一个问题.

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

答案:我猜你需要触摸对象来使某些(依赖)缓存无效.通常建议的使用touch仅适用于有限数量的"关系".例如,在更新评论时使文章无效.

我的解决方案是需要使数据库对象失效的异步集合(使用sidekiq作业).我为它编写了自己的控制逻辑,用于定义在对象更改时需要使其他对象失效的对象.例如评论==>文章.

通过这种方式,我们可以通过更冗长的方式使依赖对象无效.另外,我使用比Model.update_all"触摸链"更快的方式使其无效.它解决了我们的死锁问题(并为我们的缓存失效添加了详细程度和性能).

额外提示:不要使用updated_at.如果DB对象真的因为另一个对象发生了变化而发生变化,这是值得商榷的.覆盖cache_key模型可以轻松定义自定义缓存键"#{id}-#{valid_from}".valid_from可以是您在模型上定义的时间戳(而不是您使用的时间戳updated_at).