如何使用 Java 和 PostgreSQL 处理竞争条件-java教程

首页 2024-07-10 02:09:09

使用锁来控制数据库并发送

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

让我们编码吧!您可能会想到的第一件事就是在结账前检查库存。也许是这样:

public void validateAndDecreaseSolution(long ProductId, int 数量 {
    可选<stockentity> stockByProductId = 
 stockRepository.findStockByProductId(productId);

    int stock = stockByProductId.orElseThrow().getStock();
    int possibleStock = 库存 - 数量;

    if (股票 



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

<h3>
  
  
  第一个解决方案 - 用于更新
</h3>

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

<blockquote>
FOR UPDATE 会导致 SELECT 语句检索到的行被锁定,就像更新一样。在当前交易结束之前,它们可以被其他交易修改或删除。

</blockquote>

<pre class="brush:php;toolbar:false">@Query(value = "从股票 s 中选择 * WHERE s.product_id = ?1 FOR UPDATE", nativeQuery = true)
可选<stockentity> findStockByProductIdWithLock(Long ProductId);
</stockentity>
public void validateandecreasesolution(long ProductId, int amount) {
    可选<stockentity> stockByProductId = stockRepository.findStockByProductIdWithLock(productId);

    // ... 证实

    stockRepository.decreaseStock(productId, 数量);
}
</stockentity>

在实际交易完成之前,所有使用产品ID对stocks表格的请求都将等待。这里的目标是确保您获得股票的最新更新价值。

立即学习“Java免费学习笔记(深入);

第二种解决方案 - pg_advisory_xact_lock

这个解决方案和上一个类似,但是你可以选择什么是锁定键。我们将锁定整个交易,直到所有验证和库存减少完成。

public void acquirelockAndecreasolution(long ProductId, int amount) {
    查看nativeQuery =entityManager.createNativeQuery("选择pg_advisory_xact_lock(:lockId)");
    nativeQuery.setParameter("lockId",productId);
    nativeQuery.getSingleResult();

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

    // 必要时检查库存并抛出异常

    stockRepository.decreaseStock(productId, 数量);
}
</stockentity>

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

第三种解决方案 - WHERE 子句

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

@交易
@修改
@Query(nativeQuery = true, value = "更新库存 SET stock = stock - :quantity WHERE Product_id = :productId AND stock &gt; 0")
int reduceStockWhereQuantityGreaterThanZero(@Param("productId") Long ProductId, @Param("quantity") int amount);
结论

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

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

若要查看完整的代码,可以查看我的GitHub。

实现悲观锁定和乐观锁定的策略还有很多,比如你可以搜索更多 FOR UPDATE WITH SKIP LOCKED 的信息。

以上就是如何使用 Java 和 PostgreSQL 更多关于竞争条件的详细信息,请关注其他相关文章!


p