成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

糟糕,線上庫存竟然變成負500......

數據庫 其他數據庫
通過組合使用:Redis做第一道防線(承受80%流量);分布式鎖控制核心業務邏輯;預扣庫存+消息隊列保證最終一致性。

前言

"快看我們的秒殺系統!庫存顯示-500了!"

3年前的這個電話讓我記憶猶新。

當時某電商大促,我們自認為完美的分布式架構,在0點整瞬間被擊穿。

數據庫連接池耗盡,庫存表出現負數,客服電話被打爆...

今天這篇文章跟大家一起聊聊商品超賣的問題,希望對你會有所幫助。

1.為什么會發生超賣?

首先我們一起看看為什么會發送超賣?

1.1 數據庫的"最后防線"漏洞

我們用下面的列子,給大家介紹一下商品超賣是如何發生的。

public boolean buy(int goodsId) {
    // 1. 查詢庫存
    int stock = getStockFromDatabase(goodsId);
    if (stock > 0) {
        // 2. 扣減庫存
        updateStock(goodsId, stock - 1);
        return true;
    }
    return false;
}

在并發場景下可能變成下圖這樣的:

圖片圖片

請求1和請求2都將庫存更新成9。

根本原因:數據庫的查詢和更新操作,不是原子性校驗,多個事務可能同時通過stock>0的條件檢查。

1.2 超賣的本質

商品超賣的本質是:多個請求同時穿透緩存,同一時刻讀取到相同庫存值,最終在數據庫層發生覆蓋。

就像100個人同時看上一件衣服,都去試衣間前看了眼牌子,出來時都覺得自己應該拿到那件衣服。

2.防止超賣的方案

2.1 數據庫樂觀鎖

數據庫樂觀鎖的核心原理是通過版本號控制并發。

例如下面這樣的:

UPDATE product 
SET stock = stock -1, version=version+1 
WHERE id=123 AND version=#{currentVersion};

Java的實現代碼如下:

@Transactional
public boolean deductStock(Long productId) {
    Product product = productDao.selectForUpdate(productId);
    if (product.getStock() <= 0) return false;
    
    int affected = productDao.updateWithVersion(
        productId, 
        product.getVersion(),
        product.getStock()-1
    );
    return affected > 0;
}

基于數據庫樂觀鎖方案的架構圖如下:

圖片圖片

優缺點分析

優點

缺點

無需額外中間件

高并發時DB壓力大

實現簡單

可能出現大量更新失敗

適用場景:日訂單量1萬以下的中小系統。

2.2 Redis原子操作

Redis原子操作的核心原理是使用:Redis + Lua腳本。

核心代碼如下:

// Lua腳本保證原子性
String lua = "if redis.call('get', KEYS >= ARGV[1] then " +
             "return redis.call('decrby', KEYS[1], ARGV " +
             "else return -1 end";

public boolean preDeduct(String itemId, int count) {
    RedisScript<Long> script = new DefaultRedisScript<>(lua, Long.class);
    Long result = redisTemplate.execute(script, 
        Collections.singletonList(itemId), count);
    return result != null && result >= 0;
}

該方案的架構圖如下:

圖片圖片

性能對比

  • 單節點QPS:數據庫方案500 vs Redis方案8萬
  • 響應時間:<1ms vs 50ms+

2.3 分布式鎖

目前最常用的分布式鎖的方案是Redisson。

下面是Redisson的實現:

RLock lock = redisson.getLock("stock_lock:"+productId);
try {
    if (lock.tryLock(1, 10, TimeUnit.SECONDS)) {
        // 執行庫存操作
    }
} finally {
    lock.unlock();
}

注意事項

1.鎖粒度要細化到商品級別

2.必須設置等待時間和自動釋放

3.配合異步隊列使用效果更佳

該方案的架構圖如下:

圖片圖片

2.4 消息隊列削峰

可以使用 RocketMQ的事務消息。

核心代碼如下:

// RocketMQ事務消息示例
TransactionMQProducer producer = new TransactionMQProducer("stock_group");
producer.setExecutor(new TransactionListener() {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg) {
        // 扣減數據庫庫存
        return LocalTransactionState.COMMIT_MESSAGE;
    }
});

該方案的架構圖如下:

圖片圖片

技術指標

  • 削峰能力:10萬QPS → 2萬TPS
  • 訂單處理延遲:<1秒(正常時段)

2.5 預扣庫存

預扣庫存是防止商品超賣的終極方案。

核心算法如下:

// Guava RateLimiter限流
RateLimiter limiter = RateLimiter.create(1000); // 每秒1000個令牌

public boolean preDeduct(Long itemId) {
    if (!limiter.tryAcquire()) return false;
    
    // 寫入預扣庫存表
    preStockDao.insert(itemId, userId);
    return true;
}

該方案的架構圖如下:

圖片圖片

性能數據

  • 百萬級并發支撐能力
  • 庫存準確率99.999%
  • 訂單處理耗時200ms內

3.避坑指南

3.1 緩存與數據庫不一致

某次大促因緩存未及時失效,導致超賣1.2萬單。

錯誤示例如下:

// 錯誤示例:先刪緩存再寫庫
redisTemplate.delete("stock:"+productId);
productDao.updateStock(productId, newStock); // 存在并發寫入窗口

3.2 未考慮庫存回滾

秒殺取消后,忘記恢復庫存,引發后續超賣。

正確做法是使用事務補償。

例如下面這樣的:

@Transactional
public void cancelOrder(Order order) {
    stockDao.restock(order.getItemId(), order.getCount());
    orderDao.delete(order.getId());
}

庫存回滾和訂單刪除,在同一個事務中。

3.3 鎖粒度過大

鎖粒度過大,全局限流導致10%的請求被誤殺。

錯誤示例如下:

// 錯誤示例:全局限鎖
RLock globalLock = redisson.getLock("global_stock_lock");

總結

其實在很多大廠中,一般會將防止商品超賣的多種方案組合使用。

架構圖如下:

圖片圖片

通過組合使用:

  1. Redis做第一道防線(承受80%流量)
  2. 分布式鎖控制核心業務邏輯
  3. 預扣庫存+消息隊列保證最終一致性

實戰經驗:某電商在2023年雙11中:

  • Redis集群承載98%請求
  • 分布式鎖攔截異常流量
  • 預扣庫存保證最終準確性

系統平穩支撐了每秒12萬次秒殺請求,0超賣事故發生!

記住:沒有銀彈方案,只有適合場景的組合拳!


責任編輯:武曉燕 來源: 蘇三說技術
相關推薦

2021-03-05 18:36:59

日志Jar包代碼

2020-04-02 07:31:53

RPC超時服務端

2023-01-05 08:34:48

JDK工具

2011-07-15 16:06:16

程序員

2009-06-25 10:15:41

糟糕的程序員

2020-08-20 07:37:21

數據庫開源框架

2011-08-23 09:35:44

2023-02-13 08:14:45

2011-08-05 17:11:42

Amazon

2020-05-13 17:15:49

CPUPC處理器

2012-12-28 09:47:07

程序員代碼編程

2021-01-05 13:45:31

Go語言編程語言

2018-12-29 14:45:34

RESTfulGoogleUser

2020-01-23 15:40:00

運維架構技術

2013-07-10 11:33:17

AndroidiOS隱私

2013-09-29 13:40:21

項目

2023-05-22 15:35:10

JavaScriptWeb開發

2018-05-15 09:08:21

2023-11-23 13:07:18

代碼Golang

2024-08-06 08:08:14

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产一区在线免费观看 | 精品粉嫩aⅴ一区二区三区四区 | 亚洲精品美女视频 | 国产成人精品a视频一区www | 亚洲视频在线观看 | 大香在线伊779 | 精品一区二区三区四区 | 精品国产乱码久久久久久88av | 久久久久网站 | 欧美精品被 | 激情国产 | 久久久成人精品 | 国产精品美女久久久久久久久久久 | 黑人巨大精品 | 久草资源在线视频 | 亚洲人成在线播放 | 97精品超碰一区二区三区 | 超碰网址 | 在线国产一区 | 99re视频在线观看 | 欧美精品一区二区三区在线 | 国产99视频精品免视看9 | 欧美一区在线视频 | 中文字幕亚洲视频 | 亚洲欧美精品久久 | 一级一级毛片免费看 | 日韩欧美一区二区三区免费观看 | 久久99精品国产麻豆婷婷 | 一区二区三区在线播放 | 免费一级黄色电影 | 久久久美女| 国产成人免费一区二区60岁 | 老司机67194精品线观看 | 欧美国产日韩一区 | 欧美日韩成人一区二区 | 正在播放国产精品 | 亚洲国产精品久久久 | 久久久91精品国产一区二区三区 | 日韩午夜精品 | 亚洲a人 | 久久亚洲综合 |