如何在 postgres 中设置锁定超时 - Hibernate

Posted

技术标签:

【中文标题】如何在 postgres 中设置锁定超时 - Hibernate【英文标题】:How to set lock timeout in postgres - Hibernate 【发布时间】:2019-01-21 13:44:18 【问题描述】:

我正在尝试为我正在处理的行设置一个Lock,直到下一次提交:

entityManager.createQuery("SELECT value from Table where id=:id")
            .setParameter("id", "123")
            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
            .setHint("javax.persistence.lock.timeout", 10000)
            .getSingleResult();

我认为应该发生的是,如果两个线程同时尝试写入数据库,一个线程将在另一个之前到达更新操作,第二个线程应该等待 10 秒然后抛出PessimisticLockException

但是,无论超时设置如何,线程都会挂起,直到另一个线程完成。

看看这个例子:

database.createTransaction(transaction -> 
    // Execute the first request to the db, and lock the table
    requestAndLock(transaction);

    // open another transaction, and execute the second request in
    // a different transaction
    database.createTransaction(secondTransaction -> 
        requestAndLock(secondTransaction);
    );

    transaction.commit();
);

我预计在第二个请求中,事务将等到超时设置然后抛出PessimisticLockException,但它会永远死锁。

Hibernate 以这种方式生成我对数据库的请求:

SELECT value from Table where id=123 FOR UPDATE

在这个answer 中,我看到Postgres 只允许SELECT FOR UPDATE NO WAIT 将超时设置为0,但不能以这种方式设置超时。

还有其他方法可以与Hibernate / JPA 一起使用吗? 也许以某种方式推荐this way?

【问题讨论】:

【参考方案1】:

PostgresSQL 的锁定超时提示不适用于 PostreSQL 9.6 (.setHint("javax.persistence.lock.timeout", 10000)

我找到的唯一解决方案是取消注释 postgresql.conf 中的 lock_timeout 属性:

lock_timeout = 10000            # in milliseconds, 0 is disabled

【讨论】:

【参考方案2】:

Hibernate 支持bunch of query hints。您正在使用的设置查询超时,而不是悲观锁。查询和锁是相互独立的,需要使用下图提示。

但在您这样做之前,请注意,Hibernate 本身不会处理超时。它只将它发送到数据库,它取决于数据库,是否以及如何应用它。

要为悲观锁设置超时,您需要改用javax.persistence.lock.timeout 提示。这是一个例子:

entityManager.createQuery("SELECT value from Table where id=:id")
        .setParameter("id", "123")
        .setLockMode(LockModeType.PESSIMISTIC_WRITE)
        .setHint("javax.persistence.lock.timeout", 10000)
        .getSingleResult();

【讨论】:

打错字了,我换个问题 它不适用于 Postgres...如果为 javax.persistence.lock.timeout 提供 0... NO_WAIT 将触发但其他值 postgres 会忽略它。【参考方案3】:

我觉得你可以试试

SET LOCAL lock_timeout = '10s';
SELECT ....;

我怀疑 Hibernate 是否支持这种开箱即用的功能。您可以尝试找到一种方法来扩展它,不确定是否值得。因为我猜想在 postges 数据库(即 mvcc)上使用锁并不是最明智的选择。

您也可以在您的代码中执行 NO WAIT 并延迟重试几次。

【讨论】:

【参考方案4】:

lock_timeout 参数完全符合您的要求。

您可以在postgresql.confALTER ROLEALTER DATABASE 中为每个用户或每个数据库设置它。

【讨论】:

他只想锁定特定查询的行,而不是整个数据库或用户 有没有办法从 Hibernate 运行 SQL 语句? SET LOCAL lock_timeout = 1000

以上是关于如何在 postgres 中设置锁定超时 - Hibernate的主要内容,如果未能解决你的问题,请参考以下文章

如何在postgres中设置日期变量[重复]

如何在节点 pg 中设置 postgres 数据库和表

如何在 SQLAlchemy 中设置连接超时

如何使用 django 1.7.3/postgres 迁移在数据库中设置默认列值?

如何在 ktor 中设置会话超时?

如何在 groovy sql 中设置连接超时?