这是我参与「第五届青训营 」伴学笔记创作活动的第10天,今天学习了秒杀系统设计,下面是我的笔记。
1、秒杀系统设计架构
- 基础的三层架构,controller , service, dao
- Redis+RocketMQ中间件,优化性能
秒杀业务的特点:瞬时流量高,读多写少,实时性要求高
-
主要实现功能:
-
秒杀活动发布
-
秒杀商品详情
-
秒杀下单
2、dao层数据库设计
- 对于电商秒杀系统而言,最主要的元素是商品信息,所以商品表的设计尤为重要
- 持久化层框架使用hiberate封装SQL,在mapper.xml文件中统一编写SQL语句。完成基础的增删改查等持久化操作。
3、controller层设计
@Slf4j
@Controller
@ResponseBody
@RequestMapping("/api/v1/promo/product")
public class CategoryController {
@Resource
private HCategoryService hCategoryService;
@Resource
private HSpecDetailService hSpecDetailService;
@Resource
private OrderProducer orderProducer;
@GetMapping("/list")
public ResponseData<?> listCategory(@RequestParam Long id) {
HCategory category = hCategoryService.queryById(id);
return ResponseData.Success(category);
}
-
采用spring的依赖注入,和springmvc的注解请求开发进行controller层的开发,将service层的实体类接口使用@Resource注解注入到controller层,方便后续调用。
-
@GetMapping("/list")注解简单开发了一个url接口,表示接收--/list的get请求,@RequestParam Long id)注解表示接收前端传的/list/?id=(id),是非Restful类型的接口
-
在Java springmvc中使用@Pathvariable注解表示可以接收 /list/(id)风格的请求,这是Restful风格的请求。
-
一个controller要完成的工作有:设置接口准备接收请求,收集请求信息,解析校验参数,传参给service层方法执行业务逻辑代码,收集业务逻辑处理后的信息,根据前端需求封装service层传回的信息,响应前端。
4、service层
作为主要业务逻辑层,最为复杂,也应该是代码量最多的一层。
- 采用接口+实现类的方式
- 封装一些业务:例如缓存业务(Redis实现),Redis业务,风险控制业务(秒杀系统必备),时间业务等。
5、复杂实现
5.1、Redis介入实现秒杀下单
// 从原库存扣除活动库存
List<CreatePromoProductModel> successList = new ArrayList<>(promoProducts.size());
for (CreatePromoProductModel model : promoProducts) {
int decreaseStock = this.hSkuService.decreaseStock(model.getSkuId(), model.getPromoStock());
if (decreaseStock == 1) {
this.redisService.setVal(Constant.generatePromoStockKey(promoId, model.getSkuId(), model.getSpuId()), model.getPromoStock());
successList.add(model);
}
}
由于秒杀的并发量较大,所以采用Redis实现高并发下扣除库存的操作。之后刷新缓存,统一刷回数据库进行扣减操作。
5.2、Lua脚本实现Redis原子操作
if (redis.call('exists', KEYS[1]) == 1) then
local stock = tonumber(redis.call('get', KEYS[1]))
if (stock == -1) then
return 1
end
if (stock > 0) then
redis.call('incrby', KEYS[1], -1)
return stock - 1
end
return 0
end
return -1
lua脚本防止产生扣减数大于原库存数,用于解决超卖问题。
5.3、RocketMQ的使用
- 秒杀系统的所有请求不直接交给数据库去操作,而是先装入RocketMQ这样的一个消息队列,使用生产消费者模式,先进先出,对于消息异步消费,大幅度增加了系统的性能。
- 消息队列重试机制,确保消息成功消费,保证了缓存和数据库的数据一致性。