【问题标题】:How can I lock a set of objects for an operation?如何锁定一组对象以进行操作?
【发布时间】:2011-03-24 19:13:33
【问题描述】:

在我的 rails 应用程序中,我有一些这样的代码:

def foo
  if object_bar_exists
    raise "can't create bar twice!"
  end

  Bar.create
end

这可以由进入应用服务器的两个不同请求调用。如果这段代码由两个请求同时运行,并且它们都同时运行if检查,则两者都不会找到对方的bar,并且会创建2个bars。

为“条形集合”创建“互斥体”的最佳方法是什么?数据库中的特殊用途互斥表?

更新

我应该强调我不能在这里使用内存互斥锁,因为并发是跨请求/进程而不是线程。

【问题讨论】:

    标签: ruby-on-rails activerecord transactions locking


    【解决方案1】:

    最好的办法是在数据库事务中执行您的操作。因为您最终可能会运行多个应用程序并且它们很可能不会共享内存,所以您将无法在应用程序级别创建互斥锁,特别是如果这两个应用程序服务运行在完全不同的物理机器上时。以下是完成数据库事务的方法:

    ActiveRecord::Base.transaction do
      # Transaction code goes here.
    end
    

    如果您想确保回滚数据库事务,那么您必须在 Bar 类上启用验证,以便无效的保存请求将导致回滚:

    ActiveRecord::Base.transaction do
      bar = Bar.new(params[:bar])
      bar.save!
    end
    

    如果你已经在数据库中有一个 bar 对象,你可以像这样悲观地锁定那个对象:

    ActiveRecord::Base.transaction do
      bar = Bar.find(1, :lock => true)
      # perform operations on bar
    end
    

    【讨论】:

    • 我认为事务只能确保它们内部的所有内容都以原子方式执行。同一个事务使用代码不能由两个进程并行运行,并且有同样的问题吗? (最初的if 检查并行运行并成功,然后写入并行发生,在这两种情况下,所有各自的代码都以原子方式发生)
    • 如果您锁定了行,则不会。那是数据库中的行级锁定,因此另一个事务无法访问它。此外,如果您有确保唯一行的验证错误,那么第一次写入将成功,下一次将失败并回滚整个事务。
    • 您确实希望事务在数据库中具有唯一索引来强制解决问题。当出现问题时,事务将保持一切干净,唯一索引会在出现问题时告诉您。
    【解决方案2】:

    如果所有请求都进入同一台机器和同一台 ruby​​ 虚拟机,您可以使用 Ruby 内置的 Mutex 类:Mutex Docs

    如果有多个机器或 rvms,您将不得不使用数据库事务来创建/获取 Bar 对象,假设它以某种方式存储在 db 中。

    【讨论】:

      【解决方案3】:

      我可能会 create a Singleton 对象在 lib 目录中,这样你就只有一个事物的实例,并使用 Mutexes 锁定它。

      这样,您可以确保在任何给定时间点只有一次访问该事物。当然,任何其他请求都会阻止它,所以要记住这一点。

      对于多台机器,您必须将令牌存储在数据库中,并同步对令牌的某种访问。比如,必须查询并取出令牌,并跟踪一些数字或其他东西,以确保人们无法同时移除令牌。或者使用集中式锁定网络服务,以便您的令牌处理只在一个地方。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-10
        相关资源
        最近更新 更多