【问题标题】:How to guarantee MySQL lock acquisition order in RailsRails中如何保证MySQL锁的获取顺序
【发布时间】:2015-03-07 01:08:28
【问题描述】:

我们使用的是 Rails 4.1,并且我们是一个带有 has_many 的模型。

Model X has_many Y

Y 可以更新,然后将通过回调更新 X(增加版本字段,modified_at 等)

X也可以自行更新。

这会导致 MySQL 内部出现死锁,因为一个事务将锁定 Y1 然后想锁定 X1,而另一个事务将锁定 X1 然后想锁定 Y1。

这很容易解决,方法是在更新 Y 之前至少要求拥有 X 的 SELECT ... FOR UPDATE

如何在 Rails 中做到这一点?还是有更好的解决方案?

我正在考虑尝试保证我在 Y 上的 before_update 回调中有一个事务,然后在 X 上获得一个锁,但我不确定这是否适用于 Rails。

【问题讨论】:

  • 我建议你避免使用回调。避免死锁的真正好方法是让每个事务以相同的顺序从表中获取行锁。在您的示例中,在更新Y 中的行之前,获取父行X1 上的锁您获取Y1 上的锁之前。您仅更新X 的事务将获得X1 的锁定。通过以相同的顺序从表中获取锁,您可以避免大量死锁问题。为此,您几乎必须放弃 AR 回调。
  • 是的,这绝对是一个解决方案,但远非最佳解决方案。我想绝对减少消除这些死锁条件所需的代码更改。我似乎成功地添加了 before_update 回调以获得所有相关的锁。

标签: mysql ruby ruby-on-rails-4 rails-activerecord


【解决方案1】:

如何将模型 Y 和 X 更新包装在事务中。

X.transaction do 
 #Do Update on Y 
 #Update X
end

我没有在你的具体案例中测试过这个理论。但我认为它应该可以解决问题。让我知道它是否有效。

【讨论】:

    【解决方案2】:

    您是否尝试过使用transaction 块?

    因此,例如,您可以定义自己的save_with_transaction 方法,如下所示:

    class X < ActiveRecord::Base
      def save_with_transaction
        transaction do 
          # multiple save, update, delete calls here ...
        end
      end
    end
    

    有关 ActiveRecord here 中事务的更多文档

    【讨论】:

    • 我们有很多不同 API 控制器的代码。我想找到一种方法,只修改模型以保证锁获取顺序。就像通过回调或修补活动记录一样。
    猜你喜欢
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-27
    • 2023-01-27
    • 1970-01-01
    • 1970-01-01
    • 2015-03-18
    相关资源
    最近更新 更多