手把手教你做系统设计之秒杀系统| 青训营笔记

49 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第6天。

一、本堂课重点内容:

学习系统设计的具体流程,学习电商秒杀系统所包含的业务,掌握系统设计常遇到的挑战,学习如何解决系统常遇到的问题,根据原理编写系统程序,优化程序。

二、详细知识点介绍:

(1)系统设计方法论

场景分析(scenario):什么系统,需要哪些功能,多大的并发量
存储设计(storage):数据如何组织,sql存储,nosql存储
服务设计(service):业务功能实现和逻辑整合
可扩展性(scale):解决设计缺陷,提高鲁棒性、扩展性

(2)电商秒杀业务介绍

商品:具有交易价值和属性的信息载体。SPU:standard product unit。SKU:stock keeping unit.
秒杀业务的特点:瞬时流量高,读多写少,实时性要求高。
秒杀的挑战:资源成本,反欺诈,高性能,防止超卖,流量管控,扩展性,鲁棒性。
功能:秒杀活动的发布,秒杀商品的详情,秒杀下单。
并发:万人参与秒杀,QPS,TPS。
存储:MySQL->redis->localcache
子服务:用户服务,风控服务,活动服务,订单服务
基础组件:ID生成器,缓存组件,MQ组件,限流组件
扩展:流量隔离,CDN,缓存优化,流量管控,数据库扩展,服务水平扩展,MQ扩展,redis扩展,服务垂直扩展。

(3)课程实践

image.png

image.png

(4)课程总结

因为sql是存在硬盘中的,所以性能差,用redis可以解决这个问题,因为redis缓存在内存中。
扣减库存时要保证事务的原子性,防止抢购数量超出库存数。
用Nginx做限流。

三、实践练习例子:

(1)获取物品详情

@GetMapping("/detail")
@RateLimit(rate = RateEnum.RATE_5000_PER_SECONDS)
public ResponseData<?> getProductDetail(@RequestParam(name = "promoId") Long promoId,
                                        @RequestParam(name = "skuId") Long skuId,
                                        @RequestParam(name = "spuId") Long spuId) {

    if (promoId <= 0 || skuId <= 0 || spuId <= 0) {
        throw new BizException(ResponseEnum.ILLEGAL_ARGUMENT);
    }

    String key = Constant.generatePromoProductKey(promoId, skuId, spuId);
    HPromoProductModel promoProductModel = cacheService.getVal(key);
    if (promoProductModel == null) {
        promoProductModel = hPromotionService.getPromotionProductDetail(promoId, skuId, spuId);
    }
    if (promoProductModel == null || !promoProductModel.checkPromo()) {
        log.error("promo activity not exist, params = {}", JSON.toJSONString(promoProductModel));
        throw new BizException(ResponseEnum.ACTIVITY_NOT_EXIST);
    }

    log.info("get promo product detail success, res = {}", promoProductModel.getPromoId());
    return ResponseData.Success(promoProductModel);
}

(2)创建订单

@PostMapping("/order")
@RateLimit(rate = RateEnum.RATE_1000_PER_SECONDS)
public ResponseData<?> createOrder(@RequestBody CreateOrderRequest createOrderRequest) {

    // 登录验证 mock
    if (createOrderRequest.getUserId() == null) {
        throw new BizException(ResponseEnum.USER_NOT_LOGIN);
    }

    // risk management
    if (!riskManagementService.riskManagement(createOrderRequest.getUserId())) {
        throw new BizException(ResponseEnum.USER_UNDER_RISK);
    }

    CreateOrderModel model = createOrderRequest.convert(PromoConverter::convertCreateOrderModel);
    OrderModel order = hPromoOrderService.createOrder(model);
    return ResponseData.Success(order);
}

四、课后个人总结:

客户端直接从源站点获取数据,当服务器访问量大时会影响访问速度,进而影响用户体验,且无法保证客户端与原站点间的距离足够段,适合传输数据。CDN解决的正是如何将数据快速可靠的从原站点传递到客户端,通过CDN对数据的分发,用户可以从一个距离较近的服务器获取数据,而不是源站点,从而达到快速访问,且减少原站点负载压力的目的。

五、引用参考:

(1)CDN缓存

blog.csdn.net/qq_34629352…

(2)秒杀系统中高并发的处理

blog.csdn.net/happydecai/…