秒杀系统 | 青训营笔记

94 阅读2分钟

秒杀系统 | 青训营笔记

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

系统设计方法论

系统设计可以从以下四个方面进行:

  • 场景(什么系统、需要哪些功能)
  • 存储(数据如何组织、SQL存储、NoSQL存储)
  • 服务(业务功能实现和逻辑整合)
  • 扩展(解决设计缺陷、提高鲁棒性、扩展性)

电商秒杀业务介绍

SPU:Standard Product Unit

SKU:Stock Keeping Unit

秒杀业务特点:

  • 高性能:秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键
  • 一致性:秒杀商品减库存的实现方式同样关键,有限数量的商品在同一时刻被很多倍的请求同时来减库存,在大并发更新的过程中都要保证数据的准确性。
  • 高可用:秒杀时会在一瞬间涌入大量的流量,为了避免系统宕机,保证高可用,需要做好流量限制

秒杀系统设计:

  • 场景
    • 功能:秒杀系统发布,秒杀商品详情、秒杀下单
    • 并发:QPS 1w+、TPS 1k+
  • 存储
    • MYSQL数据库设计、Redis做缓存
  • 服务
    • 子服务:用户服务、风控服务、活动服务、订单服务
    • 基础组件:ID生成器、缓存组件、MQ组件、限流组件
  • 扩展
    • 流量隔离、CDN、缓存优化、流量管控

系统架构图:

image.png

课程实践

基本秒杀逻辑:

@Override
public int createWrongOrder(int sid) throws Exception {
    // 数据库校验库存
    Stock stock = checkStock(sid);
    // 扣库存(无锁)
    saleStock(stock);
    // 生成订单
    int res = createOrder(stock);
    return res;
}
private Stock checkStock(int sid) throws Exception {
    Stock stock = stockService.getStockById(sid);
    if (stock.getCount() < 1) {
        throw new RuntimeException("库存不足");
    }
    return stock;
}
private int saleStock(Stock stock) {
    stock.setSale(stock.getSale() + 1);
    stock.setCount(stock.getCount() - 1);
    return stockService.updateStockById(stock);
}
private int createOrder(Stock stock) throws Exception {
    StockOrder order = new StockOrder();
    order.setSid(stock.getId());
    order.setName(stock.getName());
    order.setCreateTime(new Date());
    int res = orderMapper.insertSelective(order);
    if (res == 0) {
        throw new RuntimeException("创建订单失败");
    }
    return res;
}
// 扣库存 Mapper 文件
@Update("UPDATE stock SET count = #{count, jdbcType = INTEGER}, name = #{name, jdbcType = VARCHAR}, " + "sale = #{sale,jdbcType = INTEGER},version = #{version,jdbcType = INTEGER} " + "WHERE id = #{id, jdbcType = INTEGER}")