【问题标题】:How to lock a parent record in Rails/ActiveRecord?如何在 Rails/ActiveRecord 中锁定父记录?
【发布时间】:2014-08-18 13:23:31
【问题描述】:

当有一个父记录关联多个子记录时,对父记录使用行锁定是确保一致性的明显方法。但是,我似乎找不到在 ActiveRecord 中执行此操作的干净方法。


例如,假设我们有两个模型:OrderOrderProduct

class Order < ActiveRecord::Base
  has_many :order_products
  ...
end

class OrderProduct < ActiveRecord::Base
  belongs_to :order
  ...
end

更新OrderProduct 会影响Order 的整体状态,因此我们要确保在任何给定时间只有一个事务在更新Order

如果我们在编辑 OrderProduct 时尝试实现这一点,我能看到的 ruby​​ 中最简洁的方法是:

def edit
  product = OrderProduct.find params[:id]
  Order.transaction do
    product.order.lock!
    # Make sure no changes have occurred while we were waiting for the lock
    product.reload

    # Do stuff...
    product.order.some_method
  end
end

但是,如果 SQL 查询效率很低,则会产生:

SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", "2"]]
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 2 LIMIT 1
SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 LIMIT 1 FOR UPDATE  [["id", 2]]
SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", 2]]
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 2 LIMIT 1

我们可以通过将 更改为以下内容来减少查询次数:

def edit
  product = OrderProduct.find params[:id]
  Order.transaction do
    order = Order.find product.order_id, lock: true
    # Make sure no changes have occurred while we were waiting for the lock
    product.reload
    # Cache the association
    product.order = order

    # Do stuff...
    product.order.some_method
  end
end

产生更好的 SQL:

SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", "2"]]
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 2 LIMIT 1 FOR UPDATE  [["id", 2]]
SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", 2]]

但是代码比较混乱。

有没有更简洁的方法来使用 ActiveRecord? 调用 product.order = order 只是为了缓存关联似乎有点危险。

【问题讨论】:

    标签: ruby-on-rails postgresql activerecord locking


    【解决方案1】:

    有关简单的 Rails 锁定方式,请查看http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

    【讨论】:

    • 我知道本文档中提到的方法,但它们似乎不适用于关联(参见上面的 SQL)。我已经添加了一些进一步的代码示例来提问。
    【解决方案2】:
    .lock.load
    

    是您正在寻找的。 大概?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多