黑馬點評筆記 redis實現優惠卷秒殺

文章目錄

  • 難題
  • 全局唯一ID
    • Redis實現全局唯一Id
  • 超賣問題
    • 問題
    • 解決方案
      • 樂觀鎖
        • 問題
  • 一人一單

難題

要解決優惠卷秒殺的問題我們要考慮到三個個問題,全局唯一ID超賣問題一人一單

全局唯一ID

用戶搶購時,就會生成訂單并保存到同一張表中,而訂單表如果使用數據庫自增ID就存在一些問題:

  • id的規律性太明顯
  • 受單表數據量的限制

場景分析:如果我們的id具有太明顯的規則,用戶或者說商業對手很容易猜測出來我們的一些敏感信息,比如商城在一天時間內,賣出了多少單,這明顯不合適。

場景分析:隨著我們商城規模越來越大,mysql的單表的容量不宜超過500W,數據量過大之后,我們要進行拆庫拆表,但拆分表了之后,他們從邏輯上講他們是同一張表,所以他們的id是不能一樣的, 于是乎我們需要保證id的唯一性。

全局ID生成器,是一種在分布式系統下用來生成全局唯一ID的工具,一般要滿足下列特性:
在這里插入圖片描述
為了增加ID的安全性,我們可以不直接使用Redis自增的數值,而是拼接一些其它信息:

在這里插入圖片描述
ID的組成部分:符號位:1bit,永遠為0

時間戳:31bit,以秒為單位,可以使用69年

序列號:32bit,秒內的計數器,支持每秒產生2^32個不同ID

Redis實現全局唯一Id

@Component
public class RedisIdWorker {/*** 開始時間戳*/private static final long BEGIN_TIMESTAMP = 1640995200L;/*** 序列號的位數*/private static final int COUNT_BITS = 32;private StringRedisTemplate stringRedisTemplate;public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public long nextId(String keyPrefix) {// 1.生成時間戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp = nowSecond - BEGIN_TIMESTAMP;// 2.生成序列號// 2.1.獲取當前日期,精確到天String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));// 2.2.自增長long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);// 3.拼接并返回return timestamp << COUNT_BITS | count;}
}

超賣問題

秒殺下單應該思考的內容:

下單時需要判斷兩點:

  • 秒殺是否開始或結束,如果尚未開始或已經結束則無法下單
  • 庫存是否充足,不足則無法下單

下單核心邏輯分析:

當用戶開始進行下單,我們應當去查詢優惠卷信息,查詢到優惠卷信息,判斷是否滿足秒殺條件

比如時間是否充足,如果時間充足,則進一步判斷庫存是否足夠,如果兩者都滿足,則扣減庫存,創建訂單,然后返回訂單id,如果有一個條件不滿足則直接結束。

在這里插入圖片描述


@Override
public Result seckillVoucher(Long voucherId) {// 1.查詢優惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);// 2.判斷秒殺是否開始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未開始return Result.fail("秒殺尚未開始!");}// 3.判斷秒殺是否已經結束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {// 尚未開始return Result.fail("秒殺已經結束!");}// 4.判斷庫存是否充足if (voucher.getStock() < 1) {// 庫存不足return Result.fail("庫存不足!");}//5,扣減庫存boolean success = seckillVoucherService.update().setSql("stock= stock -1").eq("voucher_id", voucherId).update();if (!success) {//扣減庫存return Result.fail("庫存不足!");}//6.創建訂單VoucherOrder voucherOrder = new VoucherOrder();// 6.1.訂單idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);// 6.2.用戶idLong userId = UserHolder.getUser().getId();voucherOrder.setUserId(userId);// 6.3.代金券idvoucherOrder.setVoucherId(voucherId);save(voucherOrder);return Result.ok(orderId);}

問題

其實按照串行的方法我們上面的代碼已經實現的解決了超賣問題,但在現實中web往往是高并發的,我們的代碼任然存在以下問題,

 if (voucher.getStock() < 1) {// 庫存不足return Result.fail("庫存不足!");}//5,扣減庫存boolean success = seckillVoucherService.update().setSql("stock= stock -1").eq("voucher_id", voucherId).update();if (!success) {//扣減庫存return Result.fail("庫存不足!");}

假設線程1過來查詢庫存,判斷出來庫存大于1,正準備去扣減庫存,但是還沒有來得及去扣減,此時線程2過來,線程2也去查詢庫存,發現這個數量一定也大于1,那么這兩個線程都會去扣減庫存,最終多個線程相當于一起去扣減庫存,此時就會出現庫存的超賣問題。
在這里插入圖片描述

解決方案

超賣問題是典型的多線程安全問題,針對這一問題的常見解決方案就是加鎖:而對于加鎖,我們通常有兩種解決方案:

  • 悲觀鎖:

    • 悲觀鎖可以實現對于數據的串行化執行,比如syn,和lock都是悲觀鎖的代表,同時,悲觀鎖中又可以再細分為公平鎖,非公平鎖,可重入鎖,等等
  • 樂觀鎖:

    • 樂觀鎖:會有一個版本號,每次操作數據會對版本號+1,再提交回數據時,會去校驗是否比之前的版本大1 ,如果大1 ,則進行操作成功,這套機制的核心邏輯在于,如果在操作過程中,版本號只比原來大1 ,那么就意味著操作過程中沒有人對他進行過修改,他的操作就是安全的,如果不大1,則數據被修改過,當然樂觀鎖還有一些變種的處理方式比如cas。

樂觀鎖

樂觀鎖解決超賣問題的核心就是版本號法,它的流程大致如下圖:
在這里插入圖片描述
代碼實現:

boolean success = seckillVoucherService.update().setSql("stock= stock -1") //set stock = stock -1.eq("voucher_id", voucherId).eq("stock",voucher.getStock()).update(); //where id = ? and stock = ?

以上邏輯的核心含義是:只要我扣減庫存時的庫存和之前我查詢到的庫存是一樣的,就意味著沒有人在中間修改過庫存,那么此時就是安全的,但是以上這種方式通過測試發現會有很多失敗的情況,失敗的原因在于:在使用樂觀鎖過程中假設100個線程同時都拿到了100的庫存,然后大家一起去進行扣減,但是100個人中只有1個人能扣減成功,其他的人在處理時,他們在扣減時,庫存已經被修改過了,所以此時其他線程都會失敗

問題

雖然以上代碼解決了超賣問題,但是代碼的效率還是太低了,因為每次用戶都需要檢測庫存是否一致,但是我們的需求要把庫存扣減最低控制到零,所以我們只需要保證庫存大于0就可以

boolean success = seckillVoucherService.update().setSql("stock= stock -1").eq("voucher_id", voucherId).update().gt("stock",0); //where id = ? and stock > 0

一人一單

惠卷是為了引流,但是目前的情況是,一個人可以無限制的搶這個優惠卷,所以我們應當增加一層邏輯,讓一個用戶只能下一個單,而不是讓一個用戶下多個單

具體操作邏輯如下:比如時間是否充足,如果時間充足,則進一步判斷庫存是否足夠,然后再根據優惠卷id和用戶id查詢是否已經下過這個訂單,如果下過這個訂單,則不再下單,否則進行下單
在這里插入圖片描述
**存在問題:**現在的問題還是和之前一樣,并發過來,查詢數據庫,都不存在訂單,所以我們還是需要加鎖,但是樂觀鎖比較適合更新數據,而現在是插入數據,所以我們需要使用悲觀鎖操作。

@Transactional
public  Result createVoucherOrder(Long voucherId) {Long userId = UserHolder.getUser().getId();synchronized(userId.toString().intern()){// 5.1.查詢訂單int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();// 5.2.判斷是否存在if (count > 0) {// 用戶已經購買過了return Result.fail("用戶已經購買過一次!");}// 6.扣減庫存boolean success = seckillVoucherService.update().setSql("stock = stock - 1") // set stock = stock - 1.eq("voucher_id", voucherId).gt("stock", 0) // where id = ? and stock > 0.update();if (!success) {// 扣減失敗return Result.fail("庫存不足!");}// 7.創建訂單VoucherOrder voucherOrder = new VoucherOrder();// 7.1.訂單idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);// 7.2.用戶idvoucherOrder.setUserId(userId);// 7.3.代金券idvoucherOrder.setVoucherId(voucherId);save(voucherOrder);// 7.返回訂單idreturn Result.ok(orderId);}
}

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

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

相關文章

【git】pip install git+https://github.com/xxx/xxx替換成本地下載編譯安裝解決網絡超時問題

目錄 &#x1f311;&#x1f311; 背景 &#x1f312; &#x1f312;作用 &#x1f314;&#x1f314; 問題 &#x1f314;&#x1f314;解決方案 &#x1f319;方法一 &#x1f319;方法二 &#x1f31d;&#x1f31d;我的解決方案 整理不易&#xff0c;歡迎一鍵三連…

7-12 統計投票情況(集合)

7-12 統計投票情況&#xff08;集合&#xff09; 分數 10 作者 python課程組 單位 福州大學至誠學院 利用集合分析活動投票情況。 第一小隊有五名隊員&#xff0c;序號是1,2,3,4,5&#xff1b;第二小隊也有五名隊員&#xff0c;序號6,7,8,9,10。 輸入一個由得票隊員編號組成的…

分布式篇---第三篇

系列文章目錄 文章目錄 系列文章目錄前言一、什么是補償事務?二、消息隊列是怎么實現的?三、那你說說Sagas事務模型前言 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到網站,這篇文章男女通用,看懂了就去分享給你的碼吧。…

qgis添加postgis數據

左側瀏覽器-PostGIS-右鍵-新建連接 展開-雙擊即可呈現 可以點擊編輯按鈕對矢量數據編輯后是直接入庫的&#xff0c;因此謹慎使用。

【DQN】基于pytorch的強化學習算法Demo

目錄 簡介代碼 簡介 DQN&#xff08;Deep Q-Network&#xff09;是一種基于深度神經網絡的強化學習算法&#xff0c;于2013年由DeepMind提出。它的目標是解決具有離散動作空間的強化學習問題&#xff0c;并在多個任務中取得了令人矚目的表現。 DQN的核心思想是使用深度神經網…

企業數字化轉型的作用是什么?_光點科技

在當今快速變化的商業環境中&#xff0c;數字化轉型已成為企業發展的重要策略。企業數字化轉型指的是利用數字技術改造傳統業務模式和管理方式&#xff0c;以提升效率、增強競爭力和創造新的增長機會。 提升運營效率&#xff1a;數字化轉型通過引入自動化工具和智能系統&#x…

指數退避重試

指數退避重試&#xff08;Exponential Backoff and Retry&#xff09;是一種網絡通信中常用的錯誤處理和重試策略。它通常用于處理臨時性的故障&#xff0c;例如網絡延遲、服務器過載或臨時性的錯誤&#xff0c;以提高系統的可靠性和穩定性。 基本思想是&#xff0c;當發生一個…

NX二次開發UF_CSYS_ask_wcs 函數介紹

文章作者&#xff1a;里海 來源網站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_ask_wcs Defined in: uf_csys.h int UF_CSYS_ask_wcs(tag_t * wcs_id ) overview 概述 Gets the object identifier of the coordinate system to which the work coordin…

JMeter壓測常見面試問題

1、JMeter可以模擬哪些類型的負載&#xff1f; JMeter可以模擬各種類型的負載&#xff0c;包括但不限于Web應用程序、API、數據庫、FTP、SMTP、JMS、SOAP / RESTful Web服務等。這使得JMeter成為一個功能強大且靈活的壓力測試工具。 2、如何配置JMeter來進行分布式壓力測試&a…

在華為昇騰開發板安裝gdal-python

作者:朱金燦 來源:clever101的專欄 為什么大多數人學不會人工智能編程?>>> 在華為昇騰開發板安裝gdal-python分為兩步:編譯gdal庫和下載gdal對應的python包。 1.編譯gdal庫 首先下載gdal庫,。在linux(arm架構)上編譯的gdal庫及其第三方庫源碼,內含一個編譯…

智慧法院 | RPA+AI打造智慧執行助手,解決“案多人少”現實難題

為深化政法智能化建設&#xff0c;加強“智慧治理”“智慧法院”“智慧檢務”“智慧警務”“智慧司法”等信息平臺建設&#xff0c;深入實施大數據戰略&#xff0c;實現科技創新成果同政法工作深度融合。法制日報社于今年3月繼續舉辦了2023政法智能化建設創新案例及論文征集宣傳…

Unity UGUI的HorizontalLayoutGroup(水平布局)組件

Horizontal Layout Group | Unity UI | 1.0.0 1. 什么是HorizontalLayoutGroup組件&#xff1f; HorizontalLayoutGroup是Unity UGUI中的一種布局組件&#xff0c;用于在水平方向上對子物體進行排列和布局。它可以根據一定的規則自動調整子物體的位置和大小&#xff0c;使它…

Shell腳本:Linux Shell腳本學習指南(第二部分Shell編程)二

第二部分&#xff1a;Shell編程&#xff08;二&#xff09; 十一、Shell數組&#xff1a;Shell數組定義以及獲取數組元素 和其他編程語言一樣&#xff0c;Shell 也支持數組。數組&#xff08;Array&#xff09;是若干數據的集合&#xff0c;其中的每一份數據都稱為元素&#…

Navicat 技術指引 | GaussDB服務器對象的創建/設計(編輯)

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持對GaussDB 主備版的管理和開發功能。它不僅具備輕松、便捷的可視化數據查看和編輯功能&#xff0c;還提供強大的高階功能&#xff08;如模型、結構同步、協同合作、數據遷移等&#xff09;&#xff0c;這…

【華為OD題庫-034】字符串化繁為簡-java

題目 給定一個輸入字符串&#xff0c;字符串只可能由英文字母(a ~ z、A ~ Z)和左右小括號()組成。當字符里存在小括號時&#xff0c;小括號是成對的&#xff0c;可以有一個或多個小括號對&#xff0c;小括號對不會嵌套&#xff0c;小括號對內可以包含1個或多個英文字母也可以不…

Jenkins Ansible 參數構建

首先在Jenkins中創建自由項目 在web端配置完成后在另一臺機子上下載nginx 在gitlab端創建項目并創建文件配置代碼 在有Jenkins的機器上下載Ansible [rootslave1 ~]# yum -y install epel-release [rootslave1 ~]# yum -y install ansible再進入下載nginx機器中克隆gitlab項目…

Android 框架層AIDL 添加接口

文章目錄 AIDL的原理構建AIDL的流程往凍結的AIDL中加接口 AIDL的原理 可以利用ALDL定義客戶端與服務均認可的編程接口&#xff0c;以便二者使用進程間通信 (IPC) 進行相互通信。在 Android 中&#xff0c;一個進程通常無法訪問另一個進程的內存。因此&#xff0c;為進行通信&a…

卷積神經網絡(AlexNet)鳥類識別

文章目錄 一、前言二、前期工作1. 設置GPU&#xff08;如果使用的是CPU可以忽略這步&#xff09;2. 導入數據3. 查看數據 二、數據預處理1. 加載數據2. 可視化數據3. 再次檢查數據4. 配置數據集 三、AlexNet (8層&#xff09;介紹四、構建AlexNet (8層&#xff09;網絡模型五、…

微信小程序image組件圖片設置最大寬度 寬高自適應

問題描述&#xff1a;在使用微信小程序image組件的時候&#xff0c;在不確定圖片寬高情況下 想給一個最大寬度讓圖片自適應&#xff0c;按比例&#xff0c;image的widthfiex和heightFiex并不能滿足&#xff08;只指定最大寬/高并不會生效&#xff09; 問題解決&#xff1a;使用…

居家適老化設計第二十九條---衛生間之花灑

無電源 燈光顯示 無障礙扶手型花灑 以上產品圖片均來源于淘寶 侵權聯系刪除 居家適老化衛生間的花灑通常具有以下特點和功能&#xff1a;1. 高度可調節&#xff1a;適老化衛生間花灑可通過調節高度&#xff0c;滿足不同身高的老年人使用需求&#xff0c;避免彎腰或過高伸展造…