秒杀系统 | 青训营笔记
这是我参与「第五届青训营」笔记创作活动的第10天。
系统设计方法论
系统设计可以从以下四个方面进行:
- 场景(什么系统、需要哪些功能)
- 存储(数据如何组织、SQL存储、NoSQL存储)
- 服务(业务功能实现和逻辑整合)
- 扩展(解决设计缺陷、提高鲁棒性、扩展性)
电商秒杀业务介绍
SPU:Standard Product Unit
SKU:Stock Keeping Unit
秒杀业务特点:
- 高性能:秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键
- 一致性:秒杀商品减库存的实现方式同样关键,有限数量的商品在同一时刻被很多倍的请求同时来减库存,在大并发更新的过程中都要保证数据的准确性。
- 高可用:秒杀时会在一瞬间涌入大量的流量,为了避免系统宕机,保证高可用,需要做好流量限制
秒杀系统设计:
- 场景
- 功能:秒杀系统发布,秒杀商品详情、秒杀下单
- 并发:QPS 1w+、TPS 1k+
- 存储
- MYSQL数据库设计、Redis做缓存
- 服务
- 子服务:用户服务、风控服务、活动服务、订单服务
- 基础组件:ID生成器、缓存组件、MQ组件、限流组件
- 扩展
- 流量隔离、CDN、缓存优化、流量管控
系统架构图:
课程实践
基本秒杀逻辑:
@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}")