目次
1 クーポンフラッシュセール機能の実装
ホームページ データベース Redis Redis クーポンのフラッシュ セールの問題を解決する方法

Redis クーポンのフラッシュ セールの問題を解決する方法

May 28, 2023 pm 02:52 PM
redis

1 クーポンフラッシュセール機能の実装

Redis クーポンのフラッシュ セールの問題を解決する方法

##注文する際には、1. フラッシュセールが開始されているか終了しているか、2. フラッシュセールが開始されているかどうかの 2 点を判断する必要があります。在庫は十分です

したがって、ビジネス ロジックは次のとおりです

1. クーポン ID からクーポン情報を取得します

2. フラッシュ セールが開始されているかどうかを判断し、開始されていない場合は、エラーメッセージが返される

#3. フラッシュセールが終了したかどうかを判断し、終了した場合はエラーメッセージを返す

#4. フラッシュセール期間内であれば、在庫があるかどうかを判断するで十分です

5. 十分な場合は、在庫を差し引きます

6. 注文情報を作成し、クーポン注文テーブルに保存します

6.1 注文IDを保存します

6.2 ユーザー ID を保存します

6.3 クーポン ID を保存します

7 .返品注文 ID

コードの実装: (サービス層の実装class)Redis クーポンのフラッシュ セールの問題を解決する方法

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>
 *  服务实现类
 * </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.获取优惠券信息
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //2.判断是否已经开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())){
            Result.fail("秒杀尚未开始!");
        }
        //3.判断是否已经结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())){
            Result.fail("秒杀已经结束了!");
        }
        //4.判断库存是否充足
        if (voucher.getStock() < 1) {
            Result.fail("库存不充足!");
        }
        //5.扣减库存
        boolean success = iSeckillVoucherService.update()
                .setSql("stock = stock-1").eq("voucher_id",voucherId)
                .update();
        if (!success){
            Result.fail("库存不充足!");
        }
        //6. 创建订单
        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添加优惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //7.返回订单id
        return Result.ok(orderId);
    }
}
ログイン後にコピー

2 売られすぎの問題 (キーポイント)

まず、上記のコードを高い同時実行性で実行してみます。 (jmx ツールを使用)

下の図は、200 のスレッドが作成され、クーポン リクエストが瞬時に発行されたことを示しています

しかし、見てみると、集計レポートでは、異常値は 45.5% にすぎないことがわかりますが、論理的には 50% であるはずです (在庫に 100 個のアイテムがあり、200 件のリクエストがここに送信されたため) Redis クーポンのフラッシュ セールの問題を解決する方法

在庫番号を見てください。それは -9Redis クーポンのフラッシュ セールの問題を解決する方法

です。注文も 109 に追加されています。これは明らかに売られすぎの問題です。 Redis クーポンのフラッシュ セールの問題を解決する方法

それでは、なぜこの問題が発生するのでしょうか?

画像を見て話してください:

通常のプロセスに従います。つまり、スレッド 1 が在庫を確認し、在庫を差し引きます。このとき、スレッド 2 が在庫をクエリします。再度在庫を差し引くので問題ありません。

売られすぎの問題は、注文 1 が在庫をチェックした後、在庫が 1 であることが判明するが、差し引かれる前にスレッド 2 も在庫をチェックすることにあります。これも 1 であることが判明し、減算も行われました (同時実行性の高いシナリオで)Redis クーポンのフラッシュ セールの問題を解決する方法

これにより、売られすぎの問題が発生しました。 Redis クーポンのフラッシュ セールの問題を解決する方法

この種の同時実行性の高い問題の最も一般的な解決策は次のとおりです:lock~

ただし、ロックには悲観的ロックと楽観的ロックが含まれます。

悲観的ロックとは、簡単に言うと、スレッドが確実に発生すると考え、操作の前に全員がロックを取得します。実行が終了したら、次の実行が順番に実行されます (シリアル実行)

楽観的ロック: 各データ変更の前に限り、スレッドの安全性を確保するために他のスレッドがデータを変更したかどうかが判断される限り、楽観的です (スレッドの安全性は決して起こらないと考えています)。

悲観的ロックは比較的単純ですが、ここでは楽観的ロックが実装されています。 Redis クーポンのフラッシュ セールの問題を解決する方法

オプティミスティック ロックの鍵は、前のクエリで取得したデータが変更されているかどうかを判断することです。一般的な方法は 2 つあります。

注意: 左側のテーブル内のデータはすべて変更されています。スレッド 1 の実行後のデータ Oh~

1。バージョン番号メソッド

# は、インベントリのクエリのステップにバージョン番号を追加することです。データを変更するたびに、バージョン番号 + 1 と判定する where 条件を追加します。バージョン番号は変更前と同じですか?

#こうすることでスレッド セーフを実現できます~

Redis クーポンのフラッシュ セールの問題を解決する方法2.CAS メソッド

##これは、バージョン番号は必要ありません。データベースを変更した後に where 条件を追加するだけで、インベントリが変更前のインベントリであるかどうかを判断します。

# 売られすぎ問題を解決するためのコード実装:

最終分析では、在庫を差し引くときに、在庫が 0

//5.1扣减库存
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>
 *  服务实现类
 * </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.获取优惠券信息
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        //2.判断是否已经开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())){
            Result.fail("秒杀尚未开始!");
        }
        //3.判断是否已经结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())){
            Result.fail("秒杀已经结束了!");
        }
        //4.判断库存是否充足
        if (voucher.getStock() < 1) {
            Result.fail("库存不充足!");
        }
        //5.扣减库存
        boolean success = iSeckillVoucherService.update()
                .setSql("stock = stock-1").eq("voucher_id",voucherId).gt("stock",0)
                .update();
        if (!success){
            Result.fail("库存不充足!");
        }
        //6. 创建订单
        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添加优惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //7.返回订单id
        return Result.ok(orderId);
    }
}
ログイン後にコピー
より大きいかどうかを判断する where 条件を追加します。

以上がRedis クーポンのフラッシュ セールの問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Redisクラスターモードの構築方法 Redisクラスターモードの構築方法 Apr 10, 2025 pm 10:15 PM

Redisクラスターモードは、シャードを介してRedisインスタンスを複数のサーバーに展開し、スケーラビリティと可用性を向上させます。構造の手順は次のとおりです。異なるポートで奇妙なRedisインスタンスを作成します。 3つのセンチネルインスタンスを作成し、Redisインスタンスを監視し、フェールオーバーを監視します。 Sentinel構成ファイルを構成し、Redisインスタンス情報とフェールオーバー設定の監視を追加します。 Redisインスタンス構成ファイルを構成し、クラスターモードを有効にし、クラスター情報ファイルパスを指定します。各Redisインスタンスの情報を含むnodes.confファイルを作成します。クラスターを起動し、CREATEコマンドを実行してクラスターを作成し、レプリカの数を指定します。クラスターにログインしてクラスター情報コマンドを実行して、クラスターステータスを確認します。作る

Redisデータをクリアする方法 Redisデータをクリアする方法 Apr 10, 2025 pm 10:06 PM

Redisデータをクリアする方法:Flushallコマンドを使用して、すべての重要な値をクリアします。 FlushDBコマンドを使用して、現在選択されているデータベースのキー値をクリアします。 [選択]を使用してデータベースを切り替え、FlushDBを使用して複数のデータベースをクリアします。 DELコマンドを使用して、特定のキーを削除します。 Redis-CLIツールを使用してデータをクリアします。

Redisキューの読み方 Redisキューの読み方 Apr 10, 2025 pm 10:12 PM

Redisのキューを読むには、キュー名を取得し、LPOPコマンドを使用して要素を読み、空のキューを処理する必要があります。特定の手順は次のとおりです。キュー名を取得します:「キュー:キュー」などの「キュー:」のプレフィックスで名前を付けます。 LPOPコマンドを使用します。キューのヘッドから要素を排出し、LPOP Queue:My-Queueなどの値を返します。空のキューの処理:キューが空の場合、LPOPはnilを返し、要素を読む前にキューが存在するかどうかを確認できます。

Redisコマンドの使用方法 Redisコマンドの使用方法 Apr 10, 2025 pm 08:45 PM

Redis指令を使用するには、次の手順が必要です。Redisクライアントを開きます。コマンド(動詞キー値)を入力します。必要なパラメーターを提供します(指示ごとに異なります)。 Enterを押してコマンドを実行します。 Redisは、操作の結果を示す応答を返します(通常はOKまたは-ERR)。

Redisロックの使用方法 Redisロックの使用方法 Apr 10, 2025 pm 08:39 PM

Redisを使用して操作をロックするには、setnxコマンドを介してロックを取得し、有効期限を設定するために有効期限コマンドを使用する必要があります。特定の手順は次のとおりです。(1)SETNXコマンドを使用して、キー価値ペアを設定しようとします。 (2)expireコマンドを使用して、ロックの有効期限を設定します。 (3)Delコマンドを使用して、ロックが不要になったときにロックを削除します。

Redisのソースコードを読み取る方法 Redisのソースコードを読み取る方法 Apr 10, 2025 pm 08:27 PM

Redisソースコードを理解する最良の方法は、段階的に進むことです。Redisの基本に精通してください。開始点として特定のモジュールまたは機能を選択します。モジュールまたは機能のエントリポイントから始めて、行ごとにコードを表示します。関数コールチェーンを介してコードを表示します。 Redisが使用する基礎となるデータ構造に精通してください。 Redisが使用するアルゴリズムを特定します。

Redisコマンドラインの使用方法 Redisコマンドラインの使用方法 Apr 10, 2025 pm 10:18 PM

Redisコマンドラインツール(Redis-Cli)を使用して、次の手順を使用してRedisを管理および操作します。サーバーに接続し、アドレスとポートを指定します。コマンド名とパラメーターを使用して、コマンドをサーバーに送信します。ヘルプコマンドを使用して、特定のコマンドのヘルプ情報を表示します。 QUITコマンドを使用して、コマンドラインツールを終了します。

Redisでデータ損失を解決する方法 Redisでデータ損失を解決する方法 Apr 10, 2025 pm 08:24 PM

Redisデータ損失の原因には、メモリの障害、停電、人的エラー、ハードウェアの障害が含まれます。ソリューションは次のとおりです。1。RDBまたはAOF持続性を使用してデータをディスクに保存します。 2。高可用性のために複数のサーバーにコピーします。 3。Hawith redis sentinelまたはredisクラスター。 4.データをバックアップするスナップショットを作成します。 5.永続性、複製、スナップショット、監視、セキュリティ対策などのベストプラクティスを実装します。

See all articles