讀寫鎖分離設(shè)計模式:提升商城系統(tǒng)庫存管理性能的利器

2024-12-30 18:33 更新

大家好,我是 V 哥。商城系統(tǒng)中,用戶在瀏覽商品詳情頁時可以查看庫存數(shù)量,這是讀操作,頻率較高。當用戶下單成功時,系統(tǒng)會更新庫存數(shù)量,這是寫操作,但相對較少。這是一個再常見不過的應(yīng)用場景了,在這種場景下,讀寫鎖分離設(shè)計模式就是最好的武器。

讀寫鎖分離設(shè)計模式是一種多線程設(shè)計模式,適合在有讀多寫少的場景中使用。它通過讀寫操作的分離,提升了系統(tǒng)的并發(fā)性和性能。在這個模式中,讀操作是共享的,可以同時被多個線程執(zhí)行,而寫操作需要獨占鎖,避免并發(fā)寫入帶來的數(shù)據(jù)不一致問題。

讀寫鎖分離設(shè)計模式的原理

咱們再來把讀寫鎖分離的原理先明確一下:

  1. 讀寫鎖(ReadWriteLock)
    • 讀寫鎖提供了兩種鎖:讀鎖和寫鎖。
    • 讀鎖是共享的,可以允許多個線程同時持有,多個線程可以并發(fā)讀取數(shù)據(jù)。
    • 寫鎖是獨占的,只有一個線程能獲取寫鎖。寫操作會阻塞所有的讀寫操作,確保數(shù)據(jù)一致性。

  1. 適用場景
    • 讀操作遠多于寫操作,數(shù)據(jù)寫入不頻繁的場景,如緩存、配置讀取、數(shù)據(jù)分析等。
    • 通過讀寫分離,可以避免讀操作阻塞,從而提高系統(tǒng)的吞吐量和并發(fā)性。

特別的愛給特別的你,滿足才是硬道理

咱們就拿在電商平臺的商品庫存管理系統(tǒng)來說,庫存數(shù)據(jù)需要滿足如下業(yè)務(wù)需求:

  1. 高并發(fā)訪問:商城有大量用戶會同時訪問商品詳情頁,查詢庫存數(shù)量。訪問這些商品庫存的用戶數(shù)是巨大的,因此讀取庫存的操作需要具備高并發(fā)能力,保證每個用戶能夠快速查詢到最新的庫存信息。

  1. 實時更新庫存:當用戶成功下單,庫存需要相應(yīng)減少。此外,可能會有后臺系統(tǒng)進行庫存更新操作,比如在補貨時增加庫存。因此,寫操作雖然較少,但必須做到線程安全,確保更新數(shù)據(jù)的準確性,避免因并發(fā)寫入導(dǎo)致庫存錯誤。

  1. 數(shù)據(jù)一致性要求:為了確保庫存數(shù)據(jù)的一致性,寫操作必須是獨占的,也就是說,只有在沒有任何讀或?qū)懖僮鲿r,系統(tǒng)才能進行寫操作,從而避免多個線程同時寫入導(dǎo)致的庫存數(shù)據(jù)錯誤。同時,也要保證在進行寫操作時,讀線程不能獲取到庫存的中間狀態(tài),確保用戶獲取的是準確的庫存信息。

  1. 讀多寫少:在實際業(yè)務(wù)中,庫存查詢的頻率遠高于更新庫存的頻率,絕大部分用戶操作僅涉及查詢商品是否有庫存,而少部分用戶操作涉及到更新庫存,比如完成下單、取消訂單、或者后臺管理員進行庫存調(diào)整。

  1. 性能要求:庫存查詢的響應(yīng)速度直接影響到用戶體驗,特別是在大型促銷活動中,大量用戶同時訪問某些熱門商品的庫存數(shù)據(jù),因此必須保證高并發(fā)讀取的性能。而寫操作因為較少,不會頻繁發(fā)生,能容忍一定的等待時間。

具體操作場景

回到功能業(yè)務(wù),通常要實現(xiàn)的具體功能場景是這樣的:

  • 庫存查詢:用戶在瀏覽商品詳情頁時,都會查看商品庫存。一個頁面可能會展示多個商品的庫存,因此多個用戶并發(fā)查詢不同商品的庫存時,系統(tǒng)需要支持多個讀線程同時讀取數(shù)據(jù),而不會互相干擾。

  • 庫存更新:當用戶下單購買商品時,系統(tǒng)需要減少相應(yīng)的庫存數(shù)量。這個寫操作必須是獨占的,以避免并發(fā)寫入導(dǎo)致庫存數(shù)量的不一致。更新操作還會在訂單取消、訂單失效等情況下發(fā)生。此外,后臺的庫存補貨也是一種寫操作,更新后的庫存應(yīng)能被用戶實時查詢到。

  • 促銷場景:在大促活動中(如雙11、黑五促銷),某些商品會有大量用戶訪問庫存。如果不進行讀寫鎖分離,頻繁的寫鎖會阻塞所有的讀線程,導(dǎo)致用戶體驗下降。因此,系統(tǒng)應(yīng)支持多個用戶同時進行讀取操作,同時保障寫入的獨占性,以平衡高并發(fā)和數(shù)據(jù)一致性的需求。

實施目標

有了這樣的場景,采用讀寫鎖分離設(shè)計模式來優(yōu)化庫存管理,可以達到以下目標:

  1. 提升讀操作的并發(fā)性:允許多個用戶并發(fā)查詢庫存,保證在讀多寫少的場景下庫存查詢的高效性。
  2. 保證寫操作的獨占性:避免并發(fā)寫入的沖突,確保庫存數(shù)據(jù)的一致性,滿足商品庫存管理系統(tǒng)的數(shù)據(jù)準確性需求。
  3. 降低讀寫沖突的等待時間:在寫操作較少的情況下,通過讀寫鎖分離有效降低讀操作的等待時間,提升系統(tǒng)整體性能。

案例:商品庫存管理

下面咱們來具體看一下案例實現(xiàn),我們要實現(xiàn)一個商品庫存管理,需求是這樣滴:

  • 多個線程可以同時讀取某商品的庫存數(shù)量。
  • 只有一個線程可以更新庫存,避免多個寫操作造成數(shù)據(jù)不一致。

一、實現(xiàn)步驟

  1. 定義商品庫存管理類 InventoryManager,使用 ReentrantReadWriteLock 進行讀寫鎖分離。
  2. 創(chuàng)建 checkStock 方法進行庫存讀取操作,獲取讀鎖。
  3. 創(chuàng)建 updateStock 方法進行庫存更新操作,獲取寫鎖。

以下是實現(xiàn)代碼:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class InventoryManager {
    // 商品庫存存儲
    private final Map<String, Integer> inventory = new HashMap<>();
    // 讀寫鎖
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();


    // 獲取庫存數(shù)量(讀操作)
    public int checkStock(String productId) {
        readLock.lock();
        try {
            return inventory.getOrDefault(productId, 0);
        } finally {
            readLock.unlock();
        }
    }


    // 更新庫存數(shù)量(寫操作)
    public void updateStock(String productId, int quantity) {
        writeLock.lock();
        try {
            int currentStock = inventory.getOrDefault(productId, 0);
            inventory.put(productId, currentStock + quantity);
            System.out.println("Updated stock for product " + productId + ": " + (currentStock + quantity));
        } finally {
            writeLock.unlock();
        }
    }
}

二、測試案例

在測試案例中,模擬多個用戶并發(fā)訪問庫存,讀取庫存的線程可以并發(fā)執(zhí)行,而更新庫存的線程會獨占鎖。

public class InventoryManagerTest {
    public static void main(String[] args) {
        InventoryManager inventoryManager = new InventoryManager();

        
        // 初始化庫存
        inventoryManager.updateStock("product_1", 100);

        
        // 模擬多個線程同時讀取庫存
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println("Stock for product_1: " + inventoryManager.checkStock("product_1"));
            }).start();
        }


        // 模擬一個線程更新庫存
        new Thread(() -> {
            inventoryManager.updateStock("product_1", -10);
            System.out.println("Stock after selling 10 units for product_1: " + inventoryManager.checkStock("product_1"));
        }).start();
    }
}

三、代碼分析

  • 讀操作 (checkStock):
    • 使用 readLock 加鎖,只需獲取讀鎖,不會影響其他讀取線程。
    • 多個讀取線程可以同時進入 checkStock 方法,提升讀取并發(fā)性。

  • 寫操作 (updateStock):
    • 使用 writeLock 加鎖,寫操作會阻塞其他讀寫操作。
    • 確保寫入操作是獨占的,防止并發(fā)寫操作導(dǎo)致的數(shù)據(jù)不一致問題。

四、運行結(jié)果示例

輸出可能如下(順序可能有所不同):

Stock for product_1: 100
Stock for product_1: 100
Stock for product_1: 100
Stock for product_1: 100
Stock for product_1: 100
Updated stock for product product_1: 90
Stock after selling 10 units for product_1: 90

五、優(yōu)缺點

  • 優(yōu)點:讀寫鎖分離允許多個讀線程并發(fā)執(zhí)行,大大提高了讀操作的效率,適合讀多寫少的場景。
  • 缺點:如果寫操作頻繁,寫鎖會阻塞讀操作,可能會降低系統(tǒng)性能,但在寫安全重要程度來看,犧牲點性能是完全可以忍受的。

到這里,你是不是可以感受到讀寫鎖分離設(shè)計模式解決了大問題了呢,并發(fā)場景下我們必須要考慮這個問題。你學(xué)沸了嗎,關(guān)注威哥愛編程,一起搞不寂寞。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號