亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Redis優(yōu)惠券秒殺解決方案

 更新時(shí)間:2022年12月06日 15:48:54   作者:芝麻干  
這篇文章主要介紹了Redis解決優(yōu)惠券秒殺應(yīng)用案例,本文先講了搶購(gòu)問題,指出其中會(huì)出現(xiàn)的多線程問題,提出解決方案采用悲觀鎖和樂觀鎖兩種方式進(jìn)行實(shí)現(xiàn),然后發(fā)現(xiàn)在搶購(gòu)過程中容易出現(xiàn)一人多單現(xiàn)象,需要的朋友可以參考下

1 實(shí)現(xiàn)優(yōu)惠券秒殺功能

下單時(shí)需要判斷兩點(diǎn):1.秒殺是否開始或者結(jié)束2.庫(kù)存是否充足

所以,我們的業(yè)務(wù)邏輯如下

1. 通過優(yōu)惠券id獲取優(yōu)惠券信息

2.判斷秒殺是否開始,如果未返回錯(cuò)誤信息

3.判斷秒殺是否結(jié)束,如果已經(jīng)結(jié)束返回錯(cuò)誤信息

4.如果在秒殺時(shí)間內(nèi),判斷庫(kù)存是否充足

5.如果充足,扣減庫(kù)存

6.創(chuàng)建訂單信息,并保存到優(yōu)惠券訂單表中

6.1 保存訂單id

6.2保存用戶id

6.3保存優(yōu)惠券id

7.返回訂單id

代碼實(shí)現(xiàn):(Service層實(shí)現(xiàn)類)

package com.hmdp.service.impl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
 * <p>
 *  服務(wù)實(shí)現(xiàn)類
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService iSeckillVoucherService;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1.獲取優(yōu)惠券信息
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //2.判斷是否已經(jīng)開始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())){
            Result.fail("秒殺尚未開始!");
        }
        //3.判斷是否已經(jīng)結(jié)束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())){
            Result.fail("秒殺已經(jīng)結(jié)束了!");
        }
        //4.判斷庫(kù)存是否充足
        if (voucher.getStock() < 1) {
            Result.fail("庫(kù)存不充足!");
        }
        //5.扣減庫(kù)存
        boolean success = iSeckillVoucherService.update()
                .setSql("stock = stock-1").eq("voucher_id",voucherId)
                .update();
        if (!success){
            Result.fail("庫(kù)存不充足!");
        }
        //6. 創(chuàng)建訂單
        VoucherOrder voucherOrder = new VoucherOrder();
        //6.1添加訂單id
        Long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //6.2添加用戶id
        Long userId = UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
        //6.3添加優(yōu)惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //7.返回訂單id
        return Result.ok(orderId);
    }
}

2 超賣問題(重點(diǎn))

我們先嘗試在高并發(fā)的情況下運(yùn)行上述代碼。(使用jmx工具)

下圖是創(chuàng)建了兩百個(gè)線程,在一瞬間發(fā)出優(yōu)惠券請(qǐng)求

但是我們看聚合報(bào)告,發(fā)現(xiàn)異常值只有45.5%,按道理來說應(yīng)該是50%(因?yàn)閹?kù)存有100個(gè),這里發(fā)出了200個(gè)請(qǐng)求)

一看庫(kù)存數(shù),好家伙,是-9

訂單也是添加了109個(gè),這顯然發(fā)生了超賣的問題。

那么,為什么會(huì)發(fā)生這種問題呢?

看圖說話:

按照我們正常的流程來走,就是線程1線查詢完庫(kù)存,然后扣減庫(kù)存,這個(gè)時(shí)候線程2再來查詢庫(kù)存,扣減庫(kù)存,這樣是沒問題的。

超賣的問題就出在,在訂單1查詢庫(kù)存后,發(fā)現(xiàn)是1,但還沒去扣減的時(shí)候,線程2也來查詢庫(kù)存,發(fā)現(xiàn)也是1,也進(jìn)行了扣減(高并發(fā)的場(chǎng)景下)

這就導(dǎo)致了超賣的問題。

對(duì)于這種高并發(fā)的問題,最常見的解決方法就是:上鎖~

但鎖又包括悲觀鎖和樂觀鎖。

悲觀鎖簡(jiǎn)單的講就是:覺得線程一定會(huì)發(fā)生,然后在操作之前每個(gè)人先拿鎖,你執(zhí)行完后,在輪到下一個(gè)來執(zhí)行(串行執(zhí)行)

樂觀鎖 :就是樂觀(認(rèn)為線程安全一定不會(huì)發(fā)生),只要在每次對(duì)數(shù)據(jù)修改之前,判斷其他線程是否對(duì)數(shù)據(jù)進(jìn)行的修改來保證線程安全。

悲觀鎖較為簡(jiǎn)單,這里實(shí)現(xiàn)樂觀鎖。

樂觀鎖的關(guān)鍵是判斷之前查詢得到的數(shù)據(jù)是否有被修改過,常見的方式有兩種

溫馨提示:左邊表格的數(shù)據(jù)都是線程1執(zhí)行后的數(shù)據(jù)哦~

1.版本號(hào)法

就是在查詢庫(kù)存的步驟上加上一個(gè)版本號(hào),每次修改完數(shù)據(jù)后給版本號(hào)+1并在后面加上where條件判斷版本號(hào)是否和修改前的一致

這樣就可以做到線程安全啦~

2.CAS法

這個(gè)就是不用版本號(hào)了,直接在修改數(shù)據(jù)庫(kù)后加上where條件判斷庫(kù)存是否是修改前的庫(kù)存

解決超賣問題代碼實(shí)現(xiàn):

說到底就是在我們扣減庫(kù)存的時(shí)候加上一個(gè)where條件判斷庫(kù)存是否大于0

//5.1扣減庫(kù)存
boolean success = iSeckillVoucherService.update()
.setSql("stock = stock-1").eq("voucher_id" , voucherId).gt("stock" ,0)
.update();
package com.hmdp.service.impl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
 * <p>
 *  服務(wù)實(shí)現(xiàn)類
 * </p>
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService iSeckillVoucherService;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1.獲取優(yōu)惠券信息
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //2.判斷是否已經(jīng)開始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())){
            Result.fail("秒殺尚未開始!");
        }
        //3.判斷是否已經(jīng)結(jié)束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())){
            Result.fail("秒殺已經(jīng)結(jié)束了!");
        }
        //4.判斷庫(kù)存是否充足
        if (voucher.getStock() < 1) {
            Result.fail("庫(kù)存不充足!");
        }
        //5.扣減庫(kù)存
        boolean success = iSeckillVoucherService.update()
                .setSql("stock = stock-1").eq("voucher_id",voucherId).gt("stock",0)
                .update();
        if (!success){
            Result.fail("庫(kù)存不充足!");
        }
        //6. 創(chuàng)建訂單
        VoucherOrder voucherOrder = new VoucherOrder();
        //6.1添加訂單id
        Long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //6.2添加用戶id
        Long userId = UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
        //6.3添加優(yōu)惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //7.返回訂單id
        return Result.ok(orderId);
    }
}

超賣問題解決

到此這篇關(guān)于Redis優(yōu)惠券秒殺解決方案的文章就介紹到這了,更多相關(guān)Redis優(yōu)惠券秒殺內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis全局ID生成器的實(shí)現(xiàn)

    Redis全局ID生成器的實(shí)現(xiàn)

    全局ID生成器,是一種在分布式系統(tǒng)下用來生成全局唯一ID的工具,本文主要介紹了Redis全局ID生成器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Redis實(shí)現(xiàn)接口防抖的示例代碼

    Redis實(shí)現(xiàn)接口防抖的示例代碼

    本文介紹了一種通過AOP、自定義注解和Redis實(shí)現(xiàn)的接口防抖技術(shù),這種方法能有效避免因網(wǎng)絡(luò)波動(dòng)等原因短時(shí)間內(nèi)發(fā)送多個(gè)請(qǐng)求導(dǎo)致的數(shù)據(jù)重復(fù)添加問題,感興趣的可以了解一下
    2024-10-10
  • Redis如何一鍵部署腳本

    Redis如何一鍵部署腳本

    這篇文章主要介紹了Redis如何一鍵部署腳本,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 從零搭建SpringBoot2.X整合Redis框架的詳細(xì)教程

    從零搭建SpringBoot2.X整合Redis框架的詳細(xì)教程

    這篇文章主要介紹了從零搭建SpringBoot2.X整合Redis框架的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Redis中的String類型及使用Redis解決訂單秒殺超賣問題

    Redis中的String類型及使用Redis解決訂單秒殺超賣問題

    這篇文章主要介紹了Redis中的String類型及使用Redis解決訂單秒殺超賣問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 關(guān)于使用IDEA的springboot框架往Redis里寫入數(shù)據(jù)亂碼問題

    關(guān)于使用IDEA的springboot框架往Redis里寫入數(shù)據(jù)亂碼問題

    這篇文章主要介紹了用IDEA的springboot框架往Redis里寫入數(shù)據(jù)亂碼問題,本文給大家分享解決方法通過圖文示例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 利用Redis進(jìn)行數(shù)據(jù)緩存的項(xiàng)目實(shí)踐

    利用Redis進(jìn)行數(shù)據(jù)緩存的項(xiàng)目實(shí)踐

    在實(shí)際的業(yè)務(wù)場(chǎng)景中,Redis 一般和其他數(shù)據(jù)庫(kù)搭配使用,用來減輕后端數(shù)據(jù)庫(kù)的壓力,本文就介紹了利用Redis進(jìn)行數(shù)據(jù)緩存的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下
    2022-06-06
  • redis數(shù)據(jù)傾斜處理方法

    redis數(shù)據(jù)傾斜處理方法

    我們?cè)谑褂肦edis分片集群時(shí),集群最好的狀態(tài)就是每個(gè)實(shí)例可以處理相同或相近比例的請(qǐng)求,但如果不是這樣,則會(huì)出現(xiàn)某些實(shí)例壓力特別大,而某些實(shí)例特別空閑的情況發(fā)生,本文就一起來看下這種情況是如何發(fā)生的以及如何處理
    2022-12-12
  • redis主從復(fù)制原理的深入講解

    redis主從復(fù)制原理的深入講解

    這篇文章主要給大家介紹了關(guān)于redis主從復(fù)制原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 解析高可用Redis服務(wù)架構(gòu)分析與搭建方案

    解析高可用Redis服務(wù)架構(gòu)分析與搭建方案

    我們按照由簡(jiǎn)至繁的步驟,搭建一個(gè)最小型的高可用的Redis服務(wù)。 本文通過四種方案給大家介紹包含每種方案的優(yōu)缺點(diǎn)及詳細(xì)解說,具體內(nèi)容詳情跟隨小編一起看看吧
    2021-06-06

最新評(píng)論