zvvq技术分享网

如何使用 Java 和 PostgreSQL 处理竞争条件(怎样使

作者:zvvq博客网
导读使用锁来控制数据库并发 想象一下您正在开发一个电子商务系统,成千上万的人试图同时购买最后剩下的产品。然而,他们中的许多人可以继续结账并完成订单。当您检查库存时,您的

zvvq

使用锁来控制数据库并发

想象一下您正在开发一个电子商务系统,成千上万的人试图同时购买最后剩下的产品。然而,他们中的许多人可以继续结账并完成订单。当您检查库存时,您的产品数量为负数。这是怎么可能的,你该如何解决这个问题?

内容来自samhan666

让我们编码吧!您可能想到的第一件事是在结帐前检查库存。也许是这样的: 本文来自zvvq

1

zvvq

2 内容来自samhan

3 内容来自samhan

4

内容来自samhan666

5

内容来自samhan

6 zvvq.cn

7

zvvq.cn

8 copyright zvvq

9

zvvq

10 zvvq好,好zvvq

11 copyright zvvq

12

zvvq.cn

13

copyright zvvq

14

zvvq

15 内容来自zvvq

16

内容来自samhan666

17 内容来自zvvq

18

zvvq好,好zvvq

19 zvvq.cn

20 zvvq好,好zvvq

21 内容来自samhan

22

copyright zvvq

23

内容来自zvvq,别采集哟

24 zvvq

25

zvvq

26 zvvq好,好zvvq

27

内容来自zvvq

28 内容来自samhan666

29

本文来自zvvq

public void validateAndDecreaseSolution(long ProductId, int 数量 {

本文来自zvvq

可选<stockentity> stockByProductId = 内容来自samhan

stockRepository.findStockByProductId(productId);

内容来自zvvq

int stock = stockByProductId.orElseThrow().getStock();

内容来自zvvq,别采集哟

int possibleStock = 库存 - 数量;

内容来自samhan

if (股票

内容来自samhan666

<p>您可以使用此验证,但是当我们谈论每秒数百、数千、数百万甚至数十个请求时,此验证是不够的。当 10 个请求同时到达这段代码并且数据库为 stockByProductId 返回相同的值时,您的代码将崩溃。在我们进行验证时,您需要一种方法来阻止其他请求。</p>

zvvq.cn

<h3>

copyright zvvq

第一个解决方案 - 用于更新 zvvq

</h3>

zvvq

<p>在 SELECT 上添加锁定语句。在此示例中,我使用 Spring Data 的 FOR UPDATE 来完成此操作。正如 PostgreSQL 文档所说</p>

zvvq.cn

<blockquote> 内容来自samhan

FOR UPDATE 会导致 SELECT 语句检索到的行被锁定,就像要进行更新一样。这可以防止它们被其他交易修改或删除,直到当前交易结束。

本文来自zvvq

</blockquote>

内容来自zvvq

<pre class="brush:php;toolbar:false">@Query(value = "从股票 s 中选择 WHERE s.product_id = ?1 FOR UPDATE", nativeQuery = true) copyright zvvq

可选<stockentity> findStockByProductIdWithLock(Long ProductId); 内容来自zvvq

</stockentity> zvvq好,好zvvq

1 本文来自zvvq

2 内容来自zvvq,别采集哟

3

内容来自samhan666

4

zvvq

5 copyright zvvq

6 zvvq.cn

7 内容来自samhan666

8 zvvq好,好zvvq

public void validateAndDecreaseSolution1(long ProductId, int amount) { 内容来自zvvq

可选<stockentity> stockByProductId = stockRepository.findStockByProductIdWithLock(productId);

内容来自samhan666

// ... 证实 内容来自samhan666

stockRepository.decreaseStock(productId, 数量); zvvq.cn

} 本文来自zvvq

</stockentity> zvvq

所有使用产品ID对stocks表的请求都将等待,直到实际交易完成。这里的目标是确保您获得股票的最新更新价值。 内容来自samhan

”;

内容来自samhan666

第二种解决方案 - pg_advisory_xact_lock

此解决方案与上一个类似,但您可以选择锁定键是什么。我们将锁定整个交易,直到完成所有验证和库存减量的处理。 本文来自zvvq

1 内容来自zvvq

2 内容来自zvvq,别采集哟

3 内容来自samhan666

4

copyright zvvq

5 内容来自samhan666

6

本文来自zvvq

7 copyright zvvq

8

内容来自zvvq,别采集哟

9

内容来自zvvq

10

内容来自samhan

11

内容来自zvvq

12

本文来自zvvq

public void acquireLockAndDecreaseSolution2(long ProductId, int amount) {

本文来自zvvq

查询nativeQuery =entityManager.createNativeQuery("选择pg_advisory_xact_lock(:lockId)"); 内容来自samhan666

nativeQuery.setParameter("lockId",productId);

内容来自samhan666

nativeQuery.getSingleResult();

内容来自zvvq,别采集哟

可选<stockentity> stockByProductId = stockRepository.findStockByProductId(productId);

内容来自zvvq

// 检查库存并在必要时抛出异常 本文来自zvvq

stockRepository.decreaseStock(productId, 数量); zvvq好,好zvvq

}

内容来自samhan

</stockentity> 内容来自samhan666

本次交易结束后,下一次请求只会与同ID的产品进行交互。

copyright zvvq

第三种解决方案 - WHERE 子句

在这种情况下,我们不会锁定行或事务。让我们允许此事务继续进行,直到更新语句为止。请注意最后一个条件:库存 > 0。这将不允许我们的库存小于零。因此,如果两个人尝试同时购买,其中一个人会收到错误,因为我们的数据库不允许库存

copyright zvvq

1 内容来自samhan

2 copyright zvvq

3 zvvq好,好zvvq

4

本文来自zvvq

@交易 zvvq.cn

@修改 内容来自samhan666

@Query(nativeQuery = true, value = "更新库存 SET stock = stock - :quantity WHERE Product_id = :productId AND stock &gt; 0")

zvvq好,好zvvq

int reduceStockWhereQuantityGreaterThanZero(@Param("productId") Long ProductId, @Param("quantity") int amount);

内容来自samhan666

结论

第一个和第二个解决方案使用悲观锁作为策略。第三是乐观锁。当您在执行涉及某个资源的任何任务时希望限制对该资源的访问时,可以使用悲观锁定策略。在您完成进程之前,目标资源将被锁定以进行任何其他访问。小心死锁! 本文来自zvvq

使用乐观锁,您可以在没有任何阻塞的情况下对同一资源执行各种查询。当冲突不太可能发生时使用它。通常,您会有一个与您的行相关的版本,当您更新该行时,数据库会将您的行版本与数据库中的行版本进行比较。如果两者相等,则更改将成功。如果没有,您必须重试。正如您所看到的,我在本文中没有使用任何版本行,但我的第三个解决方案不会阻止任何请求并使用 stock > 0 条件控制并发。

内容来自zvvq

如果想看完整代码,可以查看我的GitHub。 copyright zvvq

还有很多其他策略来实现悲观锁定和乐观锁定,例如您可以搜索更多有关 FOR UPDATE WITH SKIP LOCKED 的信息。

内容来自zvvq,别采集哟

以上就是如何使用 Java 和 PostgreSQL 处理竞争条件的详细内容,更多请关注其它相关文章!

内容来自samhan