咨询锁或NOWAIT以避免等待锁定的行?

时间:2015-10-14 15:11:20

标签: sql ruby-on-rails postgresql ruby-on-rails-4 concurrency

在我的Rails 4应用中,我对Postgres 9.4 数据库进行了此查询:

@chosen_opportunity = Opportunity.find_by_sql(
  " UPDATE \"opportunities\" s
    SET opportunity_available = false
    FROM (
          SELECT \"opportunities\".*
          FROM   \"opportunities\"
          WHERE  ( deal_id = #{@deal.id}
          AND    opportunity_available = true 
          AND    pg_try_advisory_xact_lock(id) )
          LIMIT  1
          FOR    UPDATE
          ) sub
    WHERE       s.id = sub.id
    RETURNING   sub.prize_id, sub.id"
) 

非常受this related answer on dba.SE启发。

我只是希望我的查询找到并更新第一个(随机LIMIT)行available = true,并将其更新为available = false,我需要在执行时锁定行这个,但没有让新的请求等待上一次锁的发布,因为有许多并发调用将使用此查询。

但我也看到NOWAIT的{​​{1}}选项。我不确定我是否理解使用FOR UPDATEpg_try_advisory_xact_lock()选项之间的区别,我认为他们可以实现相同的目标:

  • NOWAIT's goal

      

    要防止操作等待其他事务提交,请使用NOWAIT选项。

  • pg_try_advisory_xact_lock's goal
    不要等待上一个事务释放锁定,仍然可以执行另一个事务,只能操作下一个选择来更新“尚未锁定”的行。

哪一个更适合我的需要?

1 个答案:

答案 0 :(得分:6)

FOR UPDATE NOWAIT 只是一个好主意,如果你坚持要锁定一个特定的行, 不是 < / strong>你需要什么。您只需要任何符合条件的可用(未锁定)行。重要的区别是这个(quoting the manual for Postgres 9.4):

  

对于NOWAIT,如果无法立即锁定选定的行,则语句会报告错误,而不是等待。

相同的查询很可能会尝试锁定相同的任意选择。 FOR UPDATE NOWAIT只会豁免异常(除非你捕获错误,它将回滚整个事务),你必须重试。

我在dba.SE上引用的答案中的解决方案使用了简单的 FOR UPDATE pg_try_advisory_lock()的组合:

  

pg_try_advisory_lockpg_advisory_lock类似,但除了FOR UPDATE SKIP LOCKED   函数不会等待锁变为可用。它会   要么立即获得锁定并返回true,要么返回false   锁不能立即获得。

所以最佳选项是......第三种选择:Postgres 9.5中的新 NOWAIT ,它实现相同的行为而无需额外的函数调用。

The manual for Postgres 9.5比较了两个选项,更多地解释了这些差异:

  

防止操作等待其他事务   提交,使用SKIP LOCKEDNOWAIT选项。使用SKIP LOCKED,   语句报告错误,而不是等待,如果选定的行   无法立即锁定。使用pg_try_advisory_xact_lock(id),任何选定的行   不能立即锁定被跳过。

在Postgres 9.4或更早版本中次佳选项是将FOR UPDATEFOR UPDATE SKIP LOCKED结合使用,如参考答案所示:

(还有@IBAction func LoginPressed(sender: UIButton) { /*previously assigned by the app owner for the customer it is saved in the app code so it is in the phone memory(?)*/ if RutField.text == "243338743" && CodigoField.text == "1104"{ /*i'm not sure what this but is kind of a dialogue of what came first and later, You must previously assign the ViewController Identity on the "StoryBoard ID" it will help any doubt or image help contact me*/ dispatch_async(dispatch_get_main_queue()){ var Storyboard = UIStoryboard(name: "Main", bundle: nil) var PerfilUsuario : UIViewController = Storyboard.instantiateViewControllerWithIdentifier("PerfilUsuario") as! UIViewController self.presentViewController(PerfilUsuario, animated: true, completion: nil) } } else { TrekLogo.hidden = true } } 的实现。)

在旁边

严格地说,你是随意的,而不是真正的随机选择。这可能是一个重要的区别 您的查询的审核版本位于my answer to your other question