1. seckill アクティビティを実行するときは、次の問題に注意する必要があります。
- 売れすぎ問題:ライトニングフラッシュ製品は、在庫数が実際の販売可能数量を超えて売れすぎる可能性があり、正常に支払いができない、注文がキャンセルされるなどの問題が発生する可能性があります。したがって、過剰販売の問題を回避するには、フラッシュセールプロセス中に在庫数量を正確に管理できるようにする必要があります。
- 高い同時実行性の問題: Lightning セールでは通常、多数のユーザーが同時に購入を急ぐため、システムの同時実行性が高く、キャッシュ障害、データベースの故障、その他の問題が発生する可能性があります。そのため、同時実行性の高い問題に対処するには、システムアーキテクチャの最適化、キャッシュの増加、データベースの読み書き分離の強化などの対応策を講じる必要があります。
- 検証コードのセキュリティの問題: 悪意のある攻撃や技術的手段によるフラッシュセールへの参加を防ぐために、検証コードのメカニズムを設定することができます。確認コードはサーバーによって生成される必要があり、一意である必要があり、Web サイト上の他の場所と混在してはなりません。また、認証コードの発行には、事前に取得されないよう十分な時間をかけて判断する必要があります。
- 在庫管理: フラッシュ セール活動の在庫管理では、在庫データのリアルタイム性と正確性を確保するために、データの高速な読み取りと書き込みのためにインメモリ データベース (redis など) を使用する必要があります。
- ユーザー エクスペリエンス: seckill アクティビティを設計および実装するときは、ユーザーがスムーズかつ楽しく参加できるように、ページの読み込み速度、製品情報の明瞭さと正確さ、支払い方法の多様性とセキュリティなどのユーザー エクスペリエンスも考慮する必要があります。スパイクアクティビティ。
- 購入制限問題:ユーザーの悪意あるスナップを防ぐため、各ユーザーが一定量の商品しか購入できない、一定期間内に1回のフラッシュセールのみ参加できるなど、ユーザーごとに購入制限を課すことができます。 、など。
- アフターサービス:セクスキル活動により大量の注文や問い合わせが発生する可能性があり、注文処理、物流流通、返品・交換サービスなどのアフターサービスを事前に準備する必要があります。納期遅れの注文処理や不十分なアフターサービスなどの問題。
2.売られすぎ問題
1. 原因
seckill と過剰販売の問題は、同時実行性の高い環境で同時にサーバーに大量のリクエストが送信されることが原因で発生します。セクスキル商品の販売数量が在庫数量よりも多い場合、売れすぎ問題が発生します。
2. セマフォを使用して解決する
以下は、SpringBoot の seckill アクティビティに基づく売られすぎの問題の例です。
この例では、SpringBoot のスケジュールされたタスクのスケジューリング機能を使用して、毎日正時に seckill アクティビティを実行します。seckill を開始する前に同時アクセス量を制御する必要があるため、Semaphore セマフォを使用して制御します。具体的には、tryAcquire() メソッドを使用してセマフォを取得します。取得に成功すると、現在システムに他のユーザーがアクセスしていないことになり、seckill アクティビティを開始できます。seckill アクティビティでは、まず製品在庫を 0 に更新して seckill を開始できることを示し、次に注文を生成してデータベースに保存し、最後にセマフォを解放して他のユーザーがシステムにアクセスできるようにします。セマフォの取得中にブロックされた場合は、InterruptedException をスローして、現在のスレッドを終了する必要があります。
@Service
public class SeckillService {
@Autowired
private ProductRepository productRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private Semaphore semaphore;
@Scheduled(cron = "0 0 0 * * *") // 每天整点秒杀开始
public void startSeckill() {
List<Product> products = productRepository.findAllBySeckillStatusAndSeckillEndTimeBefore(ProductStatus.SECKILL_STATUS_OPEN, new Date());
for (Product product : products) {
try {
// 获取信号量,控制并发访问
if (semaphore.tryAcquire()) {
// 更新商品库存为0,表示可以开始秒杀
productRepository.updateSeckillStatusAndStock(product.getId(), ProductStatus.SECKILL_STATUS_SECKILLING, 0);
// 生成订单,并保存到数据库中
orderRepository.save(new Order(product, 1));
// 释放信号量,允许其他用户访问
semaphore.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}
3. 楽観的ロックを使用して解決する
この例では、楽観的ロックのメカニズムを使用して、フラッシュセールの売れすぎの問題を解決します。具体的には、まず現在の製品の在庫数量を取得し、在庫が十分であるかどうかを判断します。在庫が十分な場合は、製品在庫を 0 に更新して、seckill が成功したことを示し、注文を生成してデータベースに保存します。インベントリが不十分な場合は、現在の seckill が失敗したことを意味するため、seckill を再試行する必要があります。このプロセスでは、時間指定タスクなどの方法を使用して、成功するまでの一定時間後に seckill 操作を再度実行しました。
@Service
public class SeckillService {
@Autowired
private ProductRepository productRepository;
@Autowired
private OrderRepository orderRepository;
@Scheduled(cron = "0 0 0 * * *") // 每天整点秒杀开始
public void startSeckill() {
List<Product> products = productRepository.findAllBySeckillStatusAndSeckillEndTimeBefore(ProductStatus.SECKILL_STATUS_OPEN, new Date());
for (Product product : products) {
// 获取当前商品的库存数量
int stock = productRepository.findByProductId(product.getId()).getStock();
// 判断库存是否足够
if (stock >= 1) {
// 更新商品库存为0,表示已经秒杀成功
productRepository.updateSeckillStatusAndStock(product.getId(), ProductStatus.SECKILL_STATUS_SECKILLED, 0);
// 生成订单,并保存到数据库中
orderRepository.save(new Order(product, 1));
} else {
// 库存不足,当前秒杀失败,需要重新尝试秒杀
// 可以使用定时任务等方式,在一段时间后再次执行秒杀操作
}
}
}
}
}
4. 悲観的ロックを使用して解決する
この例では、悲観的ロック メカニズムを使用してフラッシュ販売の売れすぎ問題を解決します。具体的には、まず現在の商品の在庫数を取得し、ロック操作を行います。ここでは、ReentrantLock クラスがロック操作に使用されます。在庫が十分な場合は、製品在庫を 0 に更新して、seckill が成功したことを示し、注文を生成してデータベースに保存します。インベントリが不十分な場合は、現在の seckill が失敗したことを意味するため、seckill を再試行する必要があります。このプロセスでは、時間指定タスクなどの方法を使用して、成功するまでの一定時間後に seckill 操作を再度実行しました。
@Service
public class SeckillService {
@Autowired
private ProductRepository productRepository;
@Autowired
private OrderRepository orderRepository;
@Scheduled(cron = "0 0 0 * * *") // 每天整点秒杀开始
public void startSeckill() {
List<Product> products = productRepository.findAllBySeckillStatusAndSeckillEndTimeBefore(ProductStatus.SECKILL_STATUS_OPEN, new Date());
for (Product product : products) {
// 使用悲观锁,获取当前商品的库存数量,并进行加锁操作
Lock lock = new ReentrantLock();
synchronized(lock) {
int stock = productRepository.findByProductId(product.getId()).getStock();
if (stock >= 1) {
// 更新商品库存为0,表示已经秒杀成功
productRepository.updateSeckillStatusAndStock(product.getId(), ProductStatus.SECKILL_STATUS_SECKILLED, 0);
// 生成订单,并保存到数据库中
orderRepository.save(new Order(product, 1));
} else {
// 库存不足,当前秒杀失败,需要重新尝试秒杀
// 可以使用定时任务等方式,在一段时间后再次执行秒杀操作
}
}
}
}
}
}
3. seckill の同時実行性の高さの問題の解決策
Seckill は高度な同時実行操作であり、Seckill 操作を実行する際にはシステムの正確性と信頼性を確保する必要があります。次に、seckill の同時実行性が高い問題に対するいくつかの解決策を示します。
- 非同期操作を使用する: seckill 操作では、非同期操作を使用してデータベースへのアクセス数を削減し、それによってシステムの同時実行性を向上させることができます。たとえば、@Async アノテーションを使用して seckill 操作を非同期メソッドとしてカプセル化し、@Scheduled アノテーションを使用してスケジュールされたタスクで非同期操作を実行できます。
- キャッシュの使用: seckill 操作では、キャッシュを使用してデータベースへのアクセス数を減らすことができ、それによってシステムの同時実行性が向上します。たとえば、 @Cacheable アノテーションを使用して seckill の結果をキャッシュし、次の seckill 操作で結果をすぐに見つけられるようにすることができます。
- 分散ロックを使用する: seckill 操作では、分散ロックを使用して、同じ秒内に 1 人のユーザーのみが seckill 操作を実行できるようにすることができます。たとえば、@Lock アノテーションを使用して分散ロックを取得し、ロックを取得した後にスパイク操作を実行できます。
- データベース パーティション テーブルを使用する: seckill 操作では、データベース パーティション テーブルを使用してデータをさまざまなデータ パーティションに分散できます。これにより、データベースへのアクセス数が減り、システムの同時実行性が向上します。
- データベース クエリの最適化: seckill 操作では、データベース クエリを最適化して、システムの同時実行機能を向上させることができます。たとえば、@Query アノテーションを使用してクエリ ステートメントをカスタマイズし、データベースの同時実行機能をより効果的に活用できます。
4. seckill 検証コードのセキュリティ問題
この例では、SpringBoot のスケジュールされたタスクのスケジューリング機能を使用して、毎日正時に seckill アクティビティを実行します。seckill を開始する前に、ランダムに生成されたキャプチャを生成し、データベースに保存しました。次に、ユーザーがキャプチャを入力するのを待ち、キャプチャ ジェネレーターを使用して、ユーザーが入力したキャプチャが正しいことを確認します。検証が成功した場合は、製品在庫を 0 に更新します。これは、seckill を開始できることを示し、注文を生成してデータベースに保存します。キャプチャの検証が失敗した場合は、ユーザーのリクエストが無効であることを意味します。
@Service
public class SeckillService {
@Autowired
private ProductRepository productRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private CaptchaGenerator captchaGenerator;
@Scheduled(cron = "0 0 0 * * *") // 每天整点秒杀开始
public void startSeckill() {
List<Product> products = productRepository.findAllBySeckillStatusAndSeckillEndTimeBefore(ProductStatus.SECKILL_STATUS_OPEN, new Date());
for (Product product : products) {
// 生成验证码,并保存到数据库中
String captcha = captchaGenerator.generateCaptcha();
productRepository.updateSeckillCaptcha(product.getId(), captcha);
// 等待用户输入验证码,并进行验证
Scanner scanner = new Scanner(System.in);
System.out.println("请输入验证码:");
String inputCaptcha = scanner.nextLine();
if (captchaGenerator.validateCaptcha(inputCaptcha)) {
// 验证成功,开始秒杀操作
try {
// 更新商品库存为0,表示可以开始秒杀
productRepository.updateSeckillStatusAndStock(product.getId(), ProductStatus.SECKILL_STATUS_SECKILLING, 0);
// 生成订单,并保存到数据库中
orderRepository.save(new Order(product, 1));
} catch (Exception e) {
System.out.println("秒杀操作失败:" + e.getMessage());
}
} else {
System.out.println("验证码验证失败!");
}
}
}
}
}