【问题标题】:Querying unique records in concurrent queries with Spring使用 Spring 查询并发查询中的唯一记录
【发布时间】:2020-05-27 04:31:34
【问题描述】:

我正在尝试在 Spring Boot 应用程序中实现 Oracle 数据库锁定。这是用例:服务应查询数据库以获取与某些条件相对应的记录列表,例如

select * from books where book_status='available';

此查询返回所有available 书籍的列表,我们只返回列表中的第一本书,然后将book_status 更新为unavailable

update books set book_status='unavailable' where book_name='testName';

现在,在并发查询中,有可能 2 个查询可以返回同一本书,而这永远不会发生 - 我们应该总是返回不同的书。

为了实现这一点,我尝试使用select for update 查询:

select * from books where book_status='available' for update;

但这种方法并没有帮助我多个并发查询返回相同的记录。

我也尝试使用 JPA 来锁定记录,但这也没有帮助:

Query query = entityManager.createQuery("from Books where book_status='available'");
query.setLockMode(LockModeType.OPTIMISTIC);
query.getResultList();

有人能帮我弄清楚如何在并发查询中始终获得不同的记录吗?

【问题讨论】:

标签: spring hibernate jpa spring-data-jpa locking


【解决方案1】:

我不能 100% 确定 Select for update 返回相同的行会发生什么。但这是我目前的心理模型发生了什么:

第一个选择/线程选择并锁定行。 第二个选择相同的行并尝试获得锁,但直到第一次提交才能获得。 我希望它会看到该行的更新版本而不返回它。 但无论哪种方式,您都在阻止这似乎对您的情况没有多大意义。

乐观锁也无济于事,因为乐观锁不是传统意义上的锁。 它允许您处理数据,并且仅在将数据写回数据库时检查没有其他人更改它。 据我所知,这不是你想要的。

您需要做的是将书籍标记为正在由给定进程处理。 您可以通过以下过程做到这一点:

将前 N 行更新为当前作业正在处理。 应该像下面这样的语句:

update books b
    set book_status = 'processed by ' || :jobname
    where id in (
        select id
        from books b2 
        where book_status = 'available'
        ORDER BY name
        NEXT 10 ROWS ONLY
    ) 

在立即提交的单独事务中运行它。 Springs TransactionTemplate 通常更适合此类短期交易。

现在,作业的主要部分可以选择标记的行,而不会干扰任何其他作业,直到将 book_status 设置为 unavailable 或返回到 available,如果您想让一本书再次可用于其他作业。

如果处理发生在单个事务中,您应该能够获得与SELECT FOR UPDATE SKIP LOCKED基本相同的效果

This question 处理相同的基本问题,但角度略有不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-06
    • 1970-01-01
    • 2013-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多