首页 > 解决方案 > 如何在 Oracle 上重复此操作?(选择更新 + order by + LIMIT 1 + 跳过锁定)

问题描述

我有控制器:

@GetMapping("/old")
public Product getOld() {
    Product omeOld = productService.getOneOld();
    log.info(String.valueOf(omeOld.getId()));
    return omeOld;
}

服务:

@Override
@Transactional
public Product getOneOld() {
    Product aNew = productsRepository.findTop1ByStatusOrderByCountAsc("NEW");
    try {
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return aNew;
}

和存储库:

@Repository
public interface ProductsRepository extends JpaRepository<Product, Long> {
    Product findTop1ByStatusOrderByCountAsc(String status);
}

我启动 JMeter 并在 5 个线程中发送 5 个请求。结果我在 5 秒后得到 5 个响应。每个请求都按秒处理。但在日志中我看到下一个:

2018-09-14 14:04:35.524  INFO 9048 --- [nio-8080-exec-1] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:04:35.525  INFO 9048 --- [nio-8080-exec-2] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:04:35.532  INFO 9048 --- [nio-8080-exec-3] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:04:35.534  INFO 9048 --- [nio-8080-exec-4] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:04:35.534  INFO 9048 --- [nio-8080-exec-6] c.e.l.demo.controller.ProductController  : 1

每个线程选择同一行并处理它。我需要第一个线程选择第一行,第二个线程选择第二行等等。我尝试使用@Lock(LockModeType.PESSIMISTIC_WRITE)

@Lock(LockModeType.PESSIMISTIC_WRITE)
Product findTop1ByStatusOrderByCountAsc(String status);

现在,当我开始时,JMeter我有下一个行为:

第一个线程工作 5 秒,之后第二个线程工作 5 秒,依此类推。 25 秒所有 5 个线程。并在日志中:

2018-09-14 14:11:40.564  INFO 13724 --- [nio-8080-exec-5] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:11:45.566  INFO 13724 --- [nio-8080-exec-4] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:11:50.567  INFO 13724 --- [nio-8080-exec-2] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:11:55.568  INFO 13724 --- [nio-8080-exec-1] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:12:00.570  INFO 13724 --- [nio-8080-exec-3] c.e.l.demo.controller.ProductController  : 1

所有线程都选择同一行(如果我在第一个线程中更改此 roe - 如果条件不匹配,它将不会在第二个线程中选择)。

我试试这个:

@Query(value = "Select * from products where status = ?1 order by count asc LIMIT 1 for update", nativeQuery = true)
    Product findTop1ByStatusOrderByCountAsc(String status);

结果是一样的。

但我需要 - 第一个线程选择第一行并阻止它/第二个线程选择下一个未阻止的行并处理。我接下来尝试:

@Query(value = "Select * from products where status = ?1 order by count asc LIMIT 1 for update of products skip locked", nativeQuery = true)
    Product findTop1ByStatusOrderByCountAsc(String status);

它工作正常!:

2018-09-14 14:25:00.355  INFO 7904 --- [io-8080-exec-10] c.e.l.demo.controller.ProductController  : 4
2018-09-14 14:25:00.355  INFO 7904 --- [nio-8080-exec-4] c.e.l.demo.controller.ProductController  : 3
2018-09-14 14:25:00.355  INFO 7904 --- [nio-8080-exec-9] c.e.l.demo.controller.ProductController  : 1
2018-09-14 14:25:00.358  INFO 7904 --- [nio-8080-exec-5] c.e.l.demo.controller.ProductController  : 5
2018-09-14 14:25:00.359  INFO 7904 --- [nio-8080-exec-2] c.e.l.demo.controller.ProductController  : 6

每个线程中的每个选择都从非阻塞行中选择一行!

但是我怎么能用 Oracle 重复这个呢?在 oracle 中我不能写LIMIT 1 ,如果我使用ROWNUM = 1每个线程总是选择同一行。

标签: javaspringoracleoracle11g

解决方案


推荐阅读