控制PostgreSQL锁的持续时间等待

Fad*_*orm 23 postgresql locking

我有一张叫做的桌子 deposits

进行存款时,表格被锁定,因此查询类似于:

SELECT * FROM deposits WHERE id=123 FOR UPDATE

我假设FOR UPDATE是锁定表,以便我们可以操作它而无需另一个线程踩在数据上.

但是,当其他存款试图获取表的锁定时,会出现问题.发生的事情是,在锁定表格和调用psql_commit()某些东西之间的某个地方失败并保持锁定一段愚蠢的时间.我需要帮助解决一些问题:

  1. 尝试获取锁定的后续查询应该失败,我已经尝试实现此目的NOWAIT但是更喜欢超时方法(因为可以等待,只是不等待'愚蠢的时间')

  2. 理想情况下,我会在传递中关闭它,并让我的初始查询只保持一定时间的锁定,这可能与postgresql?

  3. 是否还有其他一些神奇的功能,我可以在查询上(类似于NOWAIT)只会在失败之前等待锁定4秒?

  4. 由于代码库令人痛苦的整体意大利面代码性质,它不仅仅是改变全局配置的问题,它有点需要是基于每个查询的解决方案

谢谢你的帮助,我会继续四处寻找,但我没有多少运气.这是psql的一个不存在的函数,因为我发现了这个:http://www.postgresql.org/message-id/40286F1F.8050703@optusnet.com.au

Cra*_*ger 53

我假设FOR UPDATE锁定表,以便我们可以操作它而不需要另一个线程踩数据.

不.FOR UPDATE只锁定那些行,这样,试图另一个事务锁定他们(用FOR SHARE,FOR UPDATE,UPDATEDELETE)阻塞,直到你的事务提交或回滚.

如果你想要一个整个表锁,阻止你可能想要的插入/更新/删除LOCK TABLE ... IN EXCLUSIVE MODE.

  1. 尝试获取锁定的后续查询应该会失败,我尝试使用NOWAIT实现这一点,但更喜欢使用超时方法(因为可以等待,只是不等待'愚蠢的时间')

    lock_timeout设置.这是在9.3中添加的,在旧版本中不可用.

    可以使用旧版本的粗略近似值statement_timeout,但这可能导致语句被不必要地取消.如果statement_timeout是1并且语句在锁上等待950ms,则它可能会获得锁并继续,只是立即被超时取消.不是你想要的.

    没有查询级别的设置方法lock_timeout,但您可以而且应该只是:

    SET LOCAL lock_timeout = '1s';

    后您BEGIN的交易.

  2. 理想情况下,我会在传递中关闭它,并让我的初始查询只保持一定时间的锁定,这可能与postgresql?

    有一种说法超时,但锁在保持交易水平.没有事务超时功能.

    如果您正在运行单语句事务,则可以statement_timeout在运行语句之前设置一个以限制它可以运行的时间.然而,这与限制锁持有多长时间并不完全相同,因为它可能等待锁定的允许1s的900ms,实际上只保持锁定100ms,然后被超时取消.

  3. 是否还有其他一些神奇的功能,我可以在查询上(类似于NOWAIT)只会在失败之前等待锁定4秒?

    不,你必须:

    BEGIN;
    SET LOCAL lock_timeout = '4s';
    SELECT ....;
    COMMIT;
    
    Run Code Online (Sandbox Code Playgroud)
  4. 由于代码库令人痛苦的整体意大利面代码性质,它不仅仅是改变全局配置的问题,它有点需要是基于每个查询的解决方案

    SET LOCAL 为此,这是合适的,也是优选的.

    在查询的文本中没有办法,它必须是一个单独的语句.

    您链接到的邮件列表帖子是一个从未实现的假想语法的提议(至少在公共PostgreSQL版本中)并且不存在.

在这种情况下,您可能需要考虑"乐观并发控制",通常称为"乐观锁定".它以更高的查询重复率和更多应用程序逻辑的需要为代价,让您更好地控制锁定行为.

  • 我在堆栈溢出时看到的最佳答案之一 - 谢谢堆! (3认同)
  • 更新:Postgres 9.5为`SELECT ... FOR UPDATE`添加了`SKIP LOCKED`选项以及现有的`NOWAIT`选项.文档:使用SKIP LOCKED,将跳过任何无法立即锁定的选定行.跳过锁定的行提供了不一致的数据视图,因此这不适用于通用工作,但可用于避免多个使用者访问类似队列的表的锁争用. (3认同)
  • @ asm0dey这是一个以毫秒为单位的时间值字段,它们都接受这样的时间表示法.参见`SELECT unit FROM pg_settings WHERE name ='lock_timeout';` (3认同)