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

大廠防止商品超賣的五種方案!

數(shù)據(jù)庫(kù) Redis
當(dāng)時(shí)某電商大促,我們自認(rèn)為完美的分布式架構(gòu),在0點(diǎn)整瞬間被擊穿。數(shù)據(jù)庫(kù)連接池耗盡,庫(kù)存表出現(xiàn)負(fù)數(shù),客服電話被打爆...

前言

"快看我們的秒殺系統(tǒng)!庫(kù)存顯示-500了!"

3年前的這個(gè)電話讓我記憶猶新。

當(dāng)時(shí)某電商大促,我們自認(rèn)為完美的分布式架構(gòu),在0點(diǎn)整瞬間被擊穿。

數(shù)據(jù)庫(kù)連接池耗盡,庫(kù)存表出現(xiàn)負(fù)數(shù),客服電話被打爆...

1.為什么會(huì)發(fā)生超賣?

首先我們一起看看為什么會(huì)發(fā)送超賣?

(1)數(shù)據(jù)庫(kù)的"最后防線"漏洞

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

-- 危險(xiǎn)的更新語句
UPDATE product SET stock = stock -1 
WHERE id=123 AND stock>0;

上面這條看似安全的SQL,在并發(fā)場(chǎng)景下可能變成下圖這樣的:

請(qǐng)求1和請(qǐng)求2都將庫(kù)存更新成9。

根本原因:數(shù)據(jù)庫(kù)的更新操作不是原子性校驗(yàn),多個(gè)事務(wù)可能同時(shí)通過stock>0的條件檢查。

(2)超賣的本質(zhì)

商品超賣的本質(zhì)是:多個(gè)請(qǐng)求同時(shí)穿透緩存,同一時(shí)刻讀取到相同庫(kù)存值,最終在數(shù)據(jù)庫(kù)層發(fā)生覆蓋。

就像100個(gè)人同時(shí)看上一件衣服,都去試衣間前看了眼牌子,出來時(shí)都覺得自己應(yīng)該拿到那件衣服。

2.防止超賣的方案

(1)數(shù)據(jù)庫(kù)樂觀鎖

數(shù)據(jù)庫(kù)樂觀鎖的核心原理是通過版本號(hào)控制并發(fā)。

例如下面這樣的:

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

Java的實(shí)現(xiàn)代碼如下:

@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;
}

基于數(shù)據(jù)庫(kù)樂觀鎖方案的架構(gòu)圖如下:

優(yōu)缺點(diǎn)分析

優(yōu)點(diǎn)

缺點(diǎn)

無需額外中間件

高并發(fā)時(shí)DB壓力大

實(shí)現(xiàn)簡(jiǎn)單

可能出現(xiàn)大量更新失敗

適用場(chǎng)景:日訂單量1萬以下的中小系統(tǒng)。

(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;
}

該方案的架構(gòu)圖如下:

性能對(duì)比

  • 單節(jié)點(diǎn)QPS:數(shù)據(jù)庫(kù)方案500 vs Redis方案8萬
  • 響應(yīng)時(shí)間:<1ms vs 50ms+

(3)分布式鎖

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

下面是Redisson的實(shí)現(xiàn):

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

注意事項(xiàng)

  • 鎖粒度要細(xì)化到商品級(jí)別
  • 必須設(shè)置等待時(shí)間和自動(dòng)釋放
  • 配合異步隊(duì)列使用效果更佳

該方案的架構(gòu)圖如下:

(4)消息隊(duì)列削峰

可以使用 RocketMQ的事務(wù)消息。

核心代碼如下:

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

該方案的架構(gòu)圖如下:

技術(shù)指標(biāo)

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

(5)預(yù)扣庫(kù)存

預(yù)扣庫(kù)存是防止商品超賣的終極方案。

核心算法如下:

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

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

該方案的架構(gòu)圖如下:

性能數(shù)據(jù)

  • 百萬級(jí)并發(fā)支撐能力
  • 庫(kù)存準(zhǔn)確率99.999%
  • 訂單處理耗時(shí)200ms內(nèi)

3.避坑指南

(1)緩存與數(shù)據(jù)庫(kù)不一致

某次大促因緩存未及時(shí)失效,導(dǎo)致超賣1.2萬單。

錯(cuò)誤示例如下:

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

(2)未考慮庫(kù)存回滾

秒殺取消后,忘記恢復(fù)庫(kù)存,引發(fā)后續(xù)超賣。

正確做法是使用事務(wù)補(bǔ)償。

例如下面這樣的:

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

庫(kù)存回滾和訂單刪除,在同一個(gè)事務(wù)中。

(3)鎖粒度過大

鎖粒度過大,全局限流導(dǎo)致10%的請(qǐng)求被誤殺。

錯(cuò)誤示例如下:

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

總結(jié)

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

架構(gòu)圖如下:

通過組合使用:

  • Redis做第一道防線(承受80%流量)
  • 分布式鎖控制核心業(yè)務(wù)邏輯
  • 預(yù)扣庫(kù)存+消息隊(duì)列保證最終一致性

實(shí)戰(zhàn)經(jīng)驗(yàn):某電商在2023年雙11中:

  • Redis集群承載98%請(qǐng)求
  • 分布式鎖攔截異常流量
  • 預(yù)扣庫(kù)存保證最終準(zhǔn)確性

系統(tǒng)平穩(wěn)支撐了每秒12萬次秒殺請(qǐng)求,0超賣事故發(fā)生!

記住:沒有銀彈方案,只有適合場(chǎng)景的組合拳!

責(zé)任編輯:姜華 來源: 蘇三說技術(shù)
相關(guān)推薦

2025-03-11 08:36:52

高并發(fā)場(chǎng)景性能

2024-07-25 09:05:35

2025-05-07 08:21:01

2021-07-09 07:21:40

SpringBootRedisLUA

2012-03-29 09:57:06

jQuery

2022-09-19 09:49:17

MCube網(wǎng)絡(luò)引擎

2022-09-03 23:18:46

Order服務(wù)負(fù)載均衡

2014-12-17 09:27:41

開源PaaS

2022-12-13 10:05:27

定時(shí)任務(wù)任務(wù)調(diào)度操作系統(tǒng)

2023-04-14 14:54:29

2025-06-09 01:22:00

2018-05-04 07:36:35

醫(yī)療行業(yè)物聯(lián)網(wǎng)IoT

2023-04-03 10:00:00

Redis分布式

2025-01-09 08:36:05

2024-10-10 10:07:07

2024-09-05 09:52:50

2018-07-09 08:38:13

集群Redis方案

2025-06-06 08:28:56

2018-12-20 10:54:49

網(wǎng)絡(luò)攻擊網(wǎng)絡(luò)安全漏洞

2011-05-23 09:32:43

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日本免费在线 | 91视频在线观看 | 日韩在线中文字幕 | 日本不卡高字幕在线2019 | www.9191| 欧美一区二区在线观看 | 日韩欧美第一页 | 天天av综合 | 免费日韩av网站 | 精品国产一区二区三区久久影院 | 日韩视频在线一区 | 久久久久久国产精品久久 | 日本h片在线观看 | 国产一区二区三区精品久久久 | 国产精品伦理一区 | 国产三级一区二区 | 久久鲁视频 | 亚洲一区中文 | 日日日日操 | sese视频在线观看 | 日韩精品免费 | 中文字幕亚洲视频 | 久久免费视频1 | 久色激情 | 欧美高清hd| 免费观看一级特黄欧美大片 | 黄色大全免费看 | h肉视频 | 91精品国产91久久久久游泳池 | 美女黄网站 | 亚洲一区二区三区四区在线观看 | 欧美久久久久久 | 久久精品亚洲欧美日韩精品中文字幕 | 成人h片在线观看 | 青青草亚洲 | 成人在线一区二区 | 亚洲逼院 | 91影片| 在线成人www免费观看视频 | 亚洲成网 | 一区二区三区四区在线视频 |