二刷 黑馬點評 秒殺優化

優化邏輯

把耗時較短的邏輯判斷放入redsi中,比如庫存是否足夠以及是否一人一單,只要這樣的邏輯完成,就代表一定能下單成功,我們就將結果返回給用戶,然后我們再開一個線程慢慢執行隊列中的信息

問題:
如何快速校驗一人一單以及庫存是否充足

交驗和下單是兩個線程,如何將二者對應:
在redis操作完成之后,會返回一些信息給前端,同時將這些信息丟給異步隊列執行,后續操作通過id來查詢下單邏輯是否完成

![[Pasted image 20250717173831.png]]

整體流程

下單后判斷是否充足只需要去redis根據key查詢對應的value是否大于0 ,如果大于0再判斷是否下過單,如果在set集合中沒有這條數據,那么就將userId和優惠卷存入redis,將優惠卷id、用戶id和訂單id存入阻塞隊列中,異步存儲到數據庫

![[Pasted image 20250717174050.png]]

stringRedisTemplate.opsForValue().set(SECKILL_STOCK_KEY + voucher.getId(), voucher.getStock().toString());
保存優惠卷并將保存秒殺的庫存到Redis

-- 3.腳本業務
-- 3.1.判斷庫存是否充足 get stockKey
if(tonumber(redis.call('get', stockKey)) <= 0) then-- 3.2.庫存不足,返回1return 1
end
-- 3.2.判斷用戶是否下單 SISMEMBER orderKey userId
if(redis.call('sismember', orderKey, userId) == 1) then-- 3.3.存在,說明是重復下單,返回2return 2
end
-- 3.6.發送消息到隊列中, XADD stream.orders * k1 v1 k2 v2 ...
redis.call('xadd', 'stream.orders', '*', 'userId', userId, 'voucherId', voucherId, 'id', orderId)

只要>0就可以下單,然后判斷用戶是否下過單

命令結構
XADD stream.orders * k1 v1 k2 v2 ...
- stream.orders:目標流的名稱。
- *:自動生成唯一的消息 ID(格式為 時間戳-序列號)。
- k1 v1 k2 v2 ...:消息的鍵值對數據。

Long result = stringRedisTemplate.execute(  SECKILL_SCRIPT, Collections.emptyList(),  voucherId.toString(), userId.toString(), String.valueOf(orderId)  
);

腳本參數
- SECKILL_SCRIPT:預定義的 Lua 腳本,處理秒殺業務邏輯(如庫存校驗、扣減)。
- Collections.emptyList():Lua 腳本中KEYS參數為空列表(無需鍵名參數)。
- 后續參數為ARGV數組,依次是voucherIduserIdorderId,供 Lua 腳本內部使用。

private static final ExecutorService SECKILL_ORDER_EXECUTOR=Executors.newSingleThreadExecutor();
定義了一個靜態常量線程池,這是一個單線程的執行器,保證任務按順序執行, 避免多線程并發處理同一用戶訂單導致的重復下單問題

// 類初始化后啟動工作線程 
@PostConstruct 
private void init() { SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler()); }

@PostConstruct確保類初始化后立即啟動訂單處理線程,并會持續運行直到應用關閉

 private class VoucherOrderHandler implements Runnable{@Overridepublic void run() {while (true){try {// 1.獲取隊列中的訂單信息VoucherOrder voucherOrder = orderTasks.take();// 2.創建訂單handleVoucherOrder(voucherOrder);} catch (Exception e) {log.error("處理訂單異常", e);}}}}

實現 Runnable 接口的類通常用于創建線程任務,可以通過 Thread 類或線程池執行。

  • orderTasks.take() 是阻塞調用,隊列空時線程會等待
  • 確保訂單按放入隊列的順序處理
  • 異常處理保證線程不會因異常終止
    proxy.createVoucherOrder(voucherOrder);
  • Spring 事務依賴 ThreadLocal,多線程環境下子線程無法獲取主線程的事務上下文
  • 通過注入代理對象proxy調用事務方法,確保事務生效
    也就是說繼承Runnable接口的類可以被在線程池中被調用
    而這里選擇了在類初始化之后就調用
Spring 事務管理的工作原理
private void handleVoucherOrder(VoucherOrder voucherOrder) {// ...獲取鎖...try {// 通過代理對象調用事務方法proxy.createVoucherOrder(voucherOrder);} finally {// ...釋放鎖...}
}

Spring 的聲明式事務是通過 AOP 代理實現的。當在方法上使用@Transactional注解時,Spring 會為該類創建一個代理對象,在調用帶注解的方法時,代理會攔截調用并添加事務管理邏輯。
如果直接使用this.createVoucherOrder(voucherOrder),事務不會生效,因為AOP代理被繞過了,必須通過代理對象調用這個方法才能觸發事務增強
因為:

  • Spring 事務是基于ThreadLocal實現的,不同線程有獨立的ThreadLocal副本
  • 子線程(如線程池中的工作線程)無法獲取主線程的事務上下文
  • 必須通過代理對象調用才能確保事務攔截器被觸發

使用AopContext.currentProxy():在方法內部獲取當前代理對象

總結

1. 為什么將庫存校驗和一人一單判斷放在 Redis 中執行?

將高頻、低耗時的校驗邏輯放在 Redis 中執行,利用其內存級別的讀寫性能和原子性操作能力,可以快速完成資格校驗。同時避免了直接訪問數據庫帶來的網絡延遲和 IO 開銷,顯著提升系統吞吐量。

2. 如何保證庫存扣減和訂單記錄的原子性?

通過 Lua 腳本在 Redis 端實現原子操作。腳本中先校驗庫存和用戶下單狀態,若滿足條件則直接扣減庫存并記錄訂單信息,整個過程不可分割,有效防止超賣和重復下單問題。

3. 異步處理訂單時,如何保證數據最終一致性?

采用消息隊列實現異步解耦,主流程完成 Redis 操作后立即返回結果,同時將訂單信息發送到阻塞隊列。獨立線程按順序處理隊列中的訂單,確保數據最終一致性。即使處理過程中出現異常,也可通過重試機制保證訂單最終入庫。

4. 為什么使用單線程執行器處理訂單隊列?

使用單線程執行器(Executors.newSingleThreadExecutor())可以確保同一用戶的訂單按順序處理,避免多線程并發處理導致的重復下單問題。同時保證了操作的順序性,與 Redis 中的校驗邏輯形成完整閉環。

5. 在多線程環境下,如何保證 Spring 事務生效?

在子線程中通過注入代理對象調用事務方法,而非直接使用this引用。因為 Spring 事務基于 AOP 代理和ThreadLocal實現,子線程無法直接獲取主線程的事務上下文。通過代理對象調用可確保事務攔截器被觸發,從而正確管理事務。

6. Redis 消息隊列相比傳統阻塞隊列有什么優勢?

Redis 的 Stream 數據結構支持持久化和多消費者組,相比 Java 內置的阻塞隊列,具有更好的可靠性和擴展性。即使服務重啟,未處理的消息也不會丟失,適合分布式系統下的異步通信場景。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89298.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89298.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89298.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HANA SQLScript中的變量類型匯總

在 SAP HANA SQLScript 中&#xff0c;可以使用多種變量類型&#xff0c;包括標量&#xff08;Scalar&#xff09;類型、表類型和結構化類型。以下是各種變量類型的詳細說明和示例。1. 標量變量&#xff08;Scalar Variables&#xff09; 標量變量是用于存儲單個值&#xff08;…

基于 Amazon Nova Sonic 和 MCP 構建語音交互 Agent

1、引言 隨著人工智能技術的飛速發展&#xff0c;自然語言處理和語音交互技術正在深刻改變人機交互的方式。語音交互正從簡單的“機械應答”向更自然的“類人對話”演進 。傳統的語音系統通常采用模塊化架構&#xff0c;將語音處理流程割裂為 ASR&#xff08;自動語音識別&…

項目的存量接口怎么低成本接入MCP?

項目的存量接口怎么低成本接入MCP&#xff1f; 老項目里的一些接口&#xff0c;如何低成本的接入MCP&#xff08;0成本不可能&#xff09;&#xff0c;變成MCP server 的tools&#xff1f; 先拋出這個問題&#xff1f;評論區的xdm如果有懂的&#xff0c;可以打在評論區&#xf…

用圖片生成高保真3D模型!Hi3DGen以法線為橋,為高清三維幾何生成另辟蹊徑

主頁&#xff1a;http://qingkeai.online/ 原文&#xff1a;用圖片生成高保真3D模型&#xff01;Hi3DGen以法線為橋&#xff0c;為高清三維幾何生成另辟蹊徑 隨著從二維圖像構建高保真三維模型的需求日益增長&#xff0c;現有方法由于域間隙的限制以及 RGB 圖像固有的模糊性&a…

Charles抓包工具中文安裝和使用詳解,快速掌握API調試與網絡優化

Charles抓包工具中文安裝和使用詳解 在軟件開發中&#xff0c;調試API請求、捕獲網絡流量以及優化應用性能是開發者日常工作中不可或缺的環節。Charles抓包工具作為業內領先的網絡調試工具&#xff0c;以其功能強大、易用性高、支持HTTPS流量解密等特點&#xff0c;廣泛應用于A…

Java :List,LinkedList,ArrayList

文章目錄List常用方法List集合的遍歷方式ArrayList底層的原理LinkedList底層原理常用方法List常用方法 //1.創建一個ArrayList集合對象&#xff08;有序、有索引、可以重復&#xff09; List<String> list new ArrayList<>(); list.add("蜘蛛精"); list…

LLM面試題及講解 4

LLM面試題及講解 4 目錄 LLM面試題及講解 4 題目講解 一、基礎概念與理論 二、模型訓練與優化 三、應用與實踐 四、前沿研究與趨勢 大型語言模型(LLM)的核心特征是什么? LLM與傳統NLP技術的本質區別是什么? Transformer架構的基本組成部分有哪些?其在LLM中為何重要? BERT…

Harmony-Next鴻蒙實戰開發項目-仿小米商城App----V2

1.、簡介 本項目是Harmony-Next原生開發&#xff0c;真實網絡請求。采用V2等狀態管理裝飾器。包含&#xff08;首頁、分類、發現、購物車、我的、登錄、搜索&#xff0c;搜索結果&#xff0c;商品詳情等&#xff09;.包含V2對接口返回數據的深度監聽。 2、頁面展示&#xff1…

python閉包和裝飾器(超詳解)

目錄 一、閉包的概念 1.概念 2.閉包的特征 3.閉包的作用 二、裝飾器 1.什么是裝飾器 2.裝飾器的作用 1.統計代碼耗時 2.對代碼進行權限檢查 3.記錄日志 3.閉包和裝飾器的關系 4.注意事項&#xff1a; 一、閉包的概念 1.概念 閉包&#xff08;Closure&#xff09;指…

解決hadoop常用到的問題

1.namenode無法啟動問題 報錯1. ERROR: Attempting to operate on hdfs namenode as root ERROR: but there is no HDFS_NAMENODE_USER defined. 原因&#xff1a;不能用 root 用戶直接啟動 Hadoop 的 HDFS 組件&#xff08;NameNode / DataNode / SecondaryNameNode&#xff0…

深度學習G3周:CGAN入門(生成手勢圖像)

&#x1f368; 本文為&#x1f517;365天深度學習訓練營中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 基礎任務&#xff1a; 1.條件生成對抗網絡&#xff08;CGAN&#xff09;的基本原理 2.CGAN是如何實現條件控制的 3.學習本文CGAN代碼&#xff0c;并跑通代碼…

流式數據處理實戰:用狀態機 + scan 優雅過濾 AI 響應中的 `<think>` 標簽

流式數據處理實戰&#xff1a;用狀態機 scan 優雅過濾 AI 響應中的 <think> 標簽 1. 引言&#xff1a;流式數據處理的挑戰 在現代 AI 應用開發中&#xff0c;流式 API&#xff08;如 OpenAI、Claude 等&#xff09;能實時返回分塊數據&#xff0c;提升用戶體驗。但流式…

【實時Linux實戰系列】硬件中斷與實時性

在實時系統中&#xff0c;硬件中斷是系統響應外部事件的關鍵機制之一。硬件中斷允許系統在執行任務時被外部事件打斷&#xff0c;從而快速響應這些事件。然而&#xff0c;中斷處理不當可能會導致系統延遲增加&#xff0c;影響系統的實時性。因此&#xff0c;優化中斷處理對于提…

基于DTLC-AEC與DTLN的輕量級實時語音降噪系統設計與實現

基于DTLC-AEC與DTLN的輕量級實時語音降噪系統設計與實現 1. 引言 在當今的實時通信應用中,語音質量是影響用戶體驗的關鍵因素之一。環境噪聲和回聲會嚴重降低語音清晰度,特別是在移動設備和嵌入式系統上。本文將詳細介紹如何將兩種先進的開源模型——DTLC-AEC(深度學習回聲…

基于Hadoop與LightFM的美妝推薦系統設計與實現

文章目錄有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主項目介紹總結每文一語有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主 項目介紹 本項目旨在基于大數據Hadoop平臺和機器學習技術&#xff0c;構建一套面向美妝…

notepad++ 多行復制拼接

如何將中文一 一復制到英文后面按住 ALT ,鼠標左鍵拖動多行選中中文Ctrl C 復制 在英文的第一行結尾處 Ctrl v 粘貼

【前沿技術動態】【AI總結】Spring Boot 4.0 預覽版深度解析:云原生時代的新里程碑

Spring Boot 4.0 預覽版深度解析&#xff1a;云原生時代的新里程碑 最低 Java 17&#xff0c;原生支持虛擬線程&#xff0c;性能提升最高800%&#xff0c;Spring Boot 4.0 帶來開發體驗與運行時性能的全面飛躍 Spring Boot 4.0 的預覽版在2025年5月底悄然上線&#xff0c;標志著…

OkHttp 框架封裝一個 HTTP 客戶端,用于調用外部服務接口

? 背景與需求 需要基于 OkHttp 框架封裝一個 HTTP 客戶端&#xff0c;用于調用外部服務接口&#xff08;如拼團回調&#xff09;&#xff0c;實現以下功能&#xff1a; 動態傳入請求地址&#xff08;URL&#xff09;支持 JSON 請求體實現類放在 infrastructure 層的 gateway…

使用Collections.max比較Map<String, Integer>中的最大值

文章目錄使用Collections.max比較Map<String, Integer>中的最大值基本方法1. 比較Map的值2. 比較Map的鍵自定義比較器1. 按值降序排列2. 復雜比較邏輯完整示例代碼性能考慮替代方案1. 使用Stream API (Java 8)2. 手動遍歷實際應用場景注意事項總結使用Collections.max比較…

鴻蒙狀態欄操作

1.鴻蒙設備基礎信息 1.1圖解 1.1窗口內容規避區域 AvoidArea7 窗口內容規避區域。 窗口內容規避區域。如系統欄區域、劉海屏區域、手勢區域、軟鍵盤區域等與窗口內容重疊時&#xff0c;需要窗口內容避讓的區域。在規避區無法響應用戶點擊事件。 除此之外還需注意規避區域的如…