」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 如何使用 Java 和 PostgreSQL 處理競爭條件

如何使用 Java 和 PostgreSQL 處理競爭條件

發佈於2024-08-01
瀏覽:215

How to deal with race conditions using Java and PostgreSQL

使用鎖來控制資料庫並發

想像一下,您正在開發一個電子商務系統,成千上萬的人試圖同時購買最後剩下的產品。然而,他們中的許多人可以繼續結帳並完成訂單。當您檢查庫存時,您的產品數量為負數。這是怎麼可能的,你該如何解決這個問題?

讓我們來編碼吧!您可能想到的第一件事是在結帳前檢查庫存。也許是這樣的:

public void validateAndDecreaseSolution(long productId, int quantity {
    Optional stockByProductId = 
 stockRepository.findStockByProductId(productId);

    int stock = stockByProductId.orElseThrow().getStock();
    int possibleStock = stock - quantity;

    if (stock 



您可以使用此驗證,但是當我們談論每秒數百、數千、數百萬甚至數十個請求時,此驗證是不夠的。當 10 個請求同時到達這段程式碼並且資料庫為 stockByProductId 傳回相同的值時,您的程式碼將會崩潰。在我們進行此驗證時,您需要一種方法來阻止其他請求。

第一個解決方案 - 用於更新

在 SELECT 上新增鎖定語句。在此範例中,我使用 Spring Data 的 FOR UPDATE 來完成此操作。正如 PostgreSQL 文件所述

FOR UPDATE 導致 SELECT 語句檢索的行被鎖定,就像要更新一樣。這可以防止它們被其他事務修改或刪除,直到目前事務結束。

@Query(value = "SELECT * FROM stocks s WHERE s.product_id = ?1 FOR UPDATE", nativeQuery = true)
Optional findStockByProductIdWithLock(Long productId);
public void validateAndDecreaseSolution1(long productId, int quantity) {
    Optional stockByProductId = stockRepository.findStockByProductIdWithLock(productId);

    // ... validate

    stockRepository.decreaseStock(productId, quantity);
}

所有使用產品ID對stocks表的請求都會等待,直到實際事務完成。這裡的目標是確保您獲得股票的最新更新價值。

第二個解決方案 - pg_advisory_xact_lock

此解決方案與上一個類似,但您可以選擇鎖定鍵是什麼。我們將鎖定整個交易,直到完成所有驗證和庫存減量的處理。

public void acquireLockAndDecreaseSolution2(long productId, int quantity) {
    Query nativeQuery = entityManager.createNativeQuery("select pg_advisory_xact_lock(:lockId)");
    nativeQuery.setParameter("lockId", productId);
    nativeQuery.getSingleResult();

    Optional stockByProductId = stockRepository.findStockByProductId(productId);

    // check stock and throws exception if it is necessary

    stockRepository.decreaseStock(productId, quantity);
}

本次交易結束後,下一次請求只會與同ID的商品互動。

第三種解決方案 - WHERE 子句

在這種情況下,我們不會鎖定行或事務。讓我們允許此事務繼續進行,直到更新語句為止。請注意最後一個條件:庫存 > 0。這將不允許我們的庫存小於零。因此,如果兩個人嘗試同時購買,其中一個人會收到錯誤,因為我們的資料庫不允許庫存

@Transactional
@Modifying
@Query(nativeQuery = true, value = "UPDATE stocks SET stock = stock - :quantity WHERE product_id = :productId AND stock > 0")
int decreaseStockWhereQuantityGreaterThanZero(@Param("productId") Long productId, @Param("quantity") int quantity);

結論

第一個和第二個解決方案使用悲觀鎖定作為策略。第三是樂觀鎖。當您在執行涉及某個資源的任何任務時希望限制對該資源的存取時,可以使用悲觀鎖定策略。在您完成進程之前,目標資源將被鎖定以進行任何其他存取。小心死鎖!

使用樂觀鎖,您可以對相同資源執行各種查詢,而不會出現任何阻塞。當衝突不太可能發生時使用它。通常,您會有一個與您的行相關的版本,當您更新該行時,資料庫會將您的行版本與資料庫中的行版本進行比較。如果兩者相等,則變更將成功。如果沒有,您必須重試。正如您所看到的,我在本文中沒有使用任何版本行,但我的第三個解決方案不會阻止任何請求並使用 stock > 0 條件控制並發性。

如果想看完整的程式碼,可以查看我的GitHub。

還有許多其他策略來實現悲觀鎖定和樂觀鎖定,例如您可以搜尋更多有關 FOR UPDATE WITH SKIP LOCKED 的內容。

版本聲明 本文轉載於:https://dev.to/ramoncunha/how-to-deal-with-race-conditions-using-java-and-postgresql-4jk6?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何在無序集合中為元組實現通用哈希功能?
    如何在無序集合中為元組實現通用哈希功能?
    在未訂購的集合中的元素要糾正此問題,一種方法是手動為特定元組類型定義哈希函數,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    程式設計 發佈於2025-04-26
  • 如何同步迭代並從PHP中的兩個等級陣列打印值?
    如何同步迭代並從PHP中的兩個等級陣列打印值?
    同步的迭代和打印值來自相同大小的兩個數組使用兩個數組相等大小的selectbox時,一個包含country代碼的數組,另一個包含鄉村代碼,另一個包含其相應名稱的數組,可能會因不當提供了exply for for for the uncore for the forsion for for ytry...
    程式設計 發佈於2025-04-26
  • HTML格式標籤
    HTML格式標籤
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    程式設計 發佈於2025-04-26
  • 為什麼在我的Linux服務器上安裝Archive_Zip後,我找不到“ class \” class \'ziparchive \'錯誤?
    為什麼在我的Linux服務器上安裝Archive_Zip後,我找不到“ class \” class \'ziparchive \'錯誤?
    class'ziparchive'在Linux Server上安裝Archive_zip時找不到錯誤 commant in lin ins in cland ins in lin.11 on a lin.1 in a lin.11錯誤:致命錯誤:在... cass中找不到類z...
    程式設計 發佈於2025-04-26
  • CSS強類型語言解析
    CSS強類型語言解析
    您可以通过其强度或弱输入的方式对编程语言进行分类的方式之一。在这里,“键入”意味着是否在编译时已知变量。一个例子是一个场景,将整数(1)添加到包含整数(“ 1”)的字符串: result = 1 "1";包含整数的字符串可能是由带有许多运动部件的复杂逻辑套件无意间生成的。它也可以是故意从单个真理...
    程式設計 發佈於2025-04-26
  • 如何配置Pytesseract以使用數字輸出的單位數字識別?
    如何配置Pytesseract以使用數字輸出的單位數字識別?
    Pytesseract OCR具有單位數字識別和僅數字約束 在pytesseract的上下文中,在配置tesseract以識別單位數字和限制單個數字和限制輸出對數字可能會提出質疑。 To address this issue, we delve into the specifics of Te...
    程式設計 發佈於2025-04-26
  • 如何使用Regex在PHP中有效地提取括號內的文本
    如何使用Regex在PHP中有效地提取括號內的文本
    php:在括號內提取文本在處理括號內的文本時,找到最有效的解決方案是必不可少的。一種方法是利用PHP的字符串操作函數,如下所示: 作為替代 $ text ='忽略除此之外的一切(text)'; preg_match('#((。 &&& [Regex使用模式來搜索特...
    程式設計 發佈於2025-04-26
  • Python不會對超範圍子串切片報錯的原因
    Python不會對超範圍子串切片報錯的原因
    在python中用索引切片範圍:二重性和空序列索引單個元素不同,該元素會引起錯誤,切片在序列的邊界之外沒有。 這種行為源於索引和切片之間的基本差異。索引一個序列,例如“示例” [3],返回一個項目。但是,切片序列(例如“示例” [3:4])返回項目的子序列。 索引不存在的元素時,例如“示例” [9...
    程式設計 發佈於2025-04-26
  • 左連接為何在右表WHERE子句過濾時像內連接?
    左連接為何在右表WHERE子句過濾時像內連接?
    左JOIN CONUNDRUM:WITCHING小時在數據庫Wizard的領域中變成內在的加入很有趣,當將c.foobar條件放置在上面的Where子句中時,據說左聯接似乎會轉換為內部連接。僅當滿足A.Foo和C.Foobar標準時,才會返回結果。 為什麼要變形?關鍵在於其中的子句。當左聯接的右側...
    程式設計 發佈於2025-04-26
  • Java為何無法創建泛型數組?
    Java為何無法創建泛型數組?
    通用陣列創建錯誤 arrayList [2]; JAVA報告了“通用數組創建”錯誤。為什麼不允許這樣做? 答案:Create an Auxiliary Class:public static ArrayList<myObject>[] a = new ArrayList<my...
    程式設計 發佈於2025-04-26
  • 圖片在Chrome中為何仍有邊框? `border: none;`無效解決方案
    圖片在Chrome中為何仍有邊框? `border: none;`無效解決方案
    在chrome 中刪除一個頻繁的問題時,在與Chrome and IE9中的圖像一起工作時,遇到了一個頻繁的問題。和“邊境:無;”在CSS中。要解決此問題,請考慮以下方法: Chrome具有忽略“ border:none; none;”的已知錯誤,風格。要解決此問題,請使用以下CSS ID塊創建帶...
    程式設計 發佈於2025-04-26
  • 為什麼Microsoft Visual C ++無法正確實現兩台模板的實例?
    為什麼Microsoft Visual C ++無法正確實現兩台模板的實例?
    The Mystery of "Broken" Two-Phase Template Instantiation in Microsoft Visual C Problem Statement:Users commonly express concerns that Micro...
    程式設計 發佈於2025-04-26
  • 如何將多種用戶類型(學生,老師和管理員)重定向到Firebase應用中的各自活動?
    如何將多種用戶類型(學生,老師和管理員)重定向到Firebase應用中的各自活動?
    Red: How to Redirect Multiple User Types to Respective ActivitiesUnderstanding the ProblemIn a Firebase-based voting app with three distinct user type...
    程式設計 發佈於2025-04-26
  • 使用jQuery如何有效修改":after"偽元素的CSS屬性?
    使用jQuery如何有效修改":after"偽元素的CSS屬性?
    在jquery中了解偽元素的限制:訪問“ selector 嘗試修改“:”選擇器的CSS屬性時,您可能會遇到困難。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    程式設計 發佈於2025-04-26
  • Go web應用何時關閉數據庫連接?
    Go web應用何時關閉數據庫連接?
    在GO Web Applications中管理數據庫連接很少,考慮以下簡化的web應用程序代碼:出現的問題:何時應在DB連接上調用Close()方法? ,該特定方案將自動關閉程序時,該程序將在EXITS EXITS EXITS出現時自動關閉。但是,其他考慮因素可能保證手動處理。 選項1:隱式關閉終...
    程式設計 發佈於2025-04-26

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3