seckill에서 주의해야 할 문제와 해결 방법

1. 세크킬 활동을 할 때 다음 사항에 유의해야 합니다.

  1. 과매도 문제 : 라이트닝 상품은 과매도, 즉 재고 수량이 실제 판매 가능 수량을 초과하여 결제 실패 또는 주문 취소 등의 문제가 발생할 수 있습니다. 따라서 과매도 문제를 방지하기 위해 플래시 세일 과정에서 재고 수량을 정확하게 제어할 수 있는지 확인해야 합니다.
  2. 높은 동시성 문제: 라이트닝 거래는 일반적으로 많은 사용자가 동시에 구매하기 위해 서두르도록 유도하여 높은 시스템 동시성, 캐시 오류, 데이터베이스 고장 및 기타 문제로 이어질 수 있습니다. 따라서 높은 동시성 문제를 해결하기 위해 시스템 아키텍처 최적화, 캐시 증가, 데이터베이스의 읽기 및 쓰기 분리 강화 등의 상응하는 조치가 필요합니다.
  3. 인증 코드 보안 문제: 악의적인 공격이나 기술적 수단이 플래시 판매에 참여하는 것을 방지하기 위해 인증 코드 메커니즘을 설정할 수 있습니다. 인증 코드는 서버에서 생성되어야 하며 고유해야 하며 웹 사이트의 다른 위치와 혼합되어서는 안 됩니다. 또한 인증코드의 발급은 충분한 판단시간을 가져야 사전에 획득되는 것을 방지할 수 있다.
  4. 재고 관리: 플래시 판매 활동의 재고 관리를 위해서는 빠른 데이터 읽기 및 쓰기를 위해 인메모리 데이터베이스(예: redis)를 사용하여 재고 데이터의 실시간 및 정확성을 보장해야 합니다.
  5. 사용자 경험: seckill 활동을 설계하고 구현할 때 사용자가 원활하고 즐겁게 참여할 수 있도록 페이지 로딩 속도, 제품 정보의 명확성과 정확성, 결제 방법의 다양성과 보안 등과 같은 사용자 경험도 고려해야 합니다. 스파이크 활동.
  6. 구매 제한 문제: 사용자가 악의적으로 스냅업하는 것을 방지하기 위해 각 사용자는 일정 수량의 상품만 구매하거나 일정 기간 동안 한 번만 플래시 세일에 참여할 수 있는 등의 구매 제한을 사용자별로 부과할 수 있습니다. , 등.
  7. 애프터서비스 : 섹킬 활동으로 대량의 주문 및 문의가 발생할 수 있으며, 사전에 주문처리, 물류유통, 반품 및 교환 등 애프터서비스를 준비하여 사전에 준비가 필요합니다. 적시 주문 처리 및 열악한 애프터 서비스와 같은 문제.

2. 과매도 문제

1. 원인

seckill과 overselling의 문제는 높은 동시성 환경에서 동시에 서버로 많은 양의 요청이 전송되기 때문에 발생합니다. 세크킬 상품의 판매수량이 재고수량보다 많은 경우 과매도 문제가 발생합니다.

2. 세마포어를 사용하여 해결

다음은 SpringBoot의 seckill 활동을 기반으로 한 과매도 문제의 예입니다.

이 예제에서는 SpringBoot의 예약된 작업 일정 기능을 사용하여 매일 정시에 seckill 활동을 실행합니다. seckill을 시작하기 전에 동시 액세스 양을 제어해야 하므로 Semaphore 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 작업을 다시 실행했습니다.

@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 작업을 다시 실행했습니다.

@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의 높은 동시성 문제에 대한 몇 가지 솔루션입니다.

  1. 비동기 작업 사용: seckill 작업에서 비동기 작업을 사용하여 데이터베이스에 대한 액세스 수를 줄여 시스템의 동시성을 향상시킬 수 있습니다. 예를 들어 @Async 주석을 사용하여 seckill 작업을 비동기 메서드로 캡슐화하고 @Scheduled 주석을 사용하여 예약된 작업에서 비동기 작업을 수행할 수 있습니다.
  2. 캐시 사용: seckill 작업에서 캐시를 사용하여 데이터베이스에 대한 액세스 수를 줄여 시스템의 동시성을 향상시킬 수 있습니다. 예를 들어 @Cacheable 주석을 사용하여 다음 seckill 작업에서 결과를 빠르게 찾을 수 있도록 seckill 결과를 캐시할 수 있습니다.
  3. 분산 잠금 사용: seckill 작업에서 분산 잠금을 사용하여 한 사용자만 동일한 초 내에 seckill 작업을 수행할 수 있도록 할 수 있습니다. 예를 들어 @Lock 주석을 사용하여 분산 잠금을 획득하고 잠금을 획득한 후 스파이크 작업을 수행할 수 있습니다.
  4. 데이터베이스 파티션 테이블 사용: seckill 작업에서 데이터베이스 파티션 테이블을 사용하여 데이터를 다른 데이터 파티션으로 분산함으로써 데이터베이스에 대한 액세스 수를 줄이고 시스템의 동시성을 향상시킬 수 있습니다.
  5. 데이터베이스 쿼리 최적화: 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("验证码验证失败!");  
            }  
        }  
    }  
}  
}  

추천

출처blog.csdn.net/lovoo/article/details/131510006