1. 今天做了什么
- Redis秒杀优化:使用Redis + Lua脚本 + 阻塞队列实现异步秒杀下单
2. 核心知识点
原来的业务逻辑存在以下问题影响性能:
- 2次数据库查询 + 2次修改数据库,数据库操作比较耗时间
- 一次请求,所有操作串行执行,等到全部执行完才能返回订单id
优化思路:
- 使用Redis提前缓存优惠券库存(新增秒杀券时),并使用set维护优惠券已下单用户
- 将判断库存与一人一单检查全部使用lua脚本执行
- 判断库存与一人一单检查通过,直接返回订单id
- 将优惠券id、用户id、订单添加到阻塞队列
- 开启异步线程,基于阻塞队列实现异步下单



lua脚本:
1 | -- 优惠券id |
- 由于lua脚本中已经完成了对一人一单的判断,过程是原子操作,且Redis是单线程的,不存在线程安全问题,所以后续库存扣减与订单保存不需要判断一人一单,也就不需要加锁
阻塞队列:获取时如果队列为空,则阻塞等待,直到有元素加入队列,则返回该元素
1 | //阻塞队列,获取时如果队列为空,则阻塞等待,直到有元素加入队列,则返回该元素 |
3. 深度思考
基于阻塞队列实现异步下单会有的问题:
- 内存限制:阻塞队列存储在内存中,内存空间有限
- 数据安全问题:服务突然宕机,内存数据会丢失
4. 踩过的坑
- 在编码过程中,在异步线程中使用
Long userId = UserHolder.getUser().getId();造成了异常,因为ThreadLocal是基于线程的,异步线程获取不到主线程存储的用户信息
5. 遗留问题 / 想不通
- 以上优化未对秒杀时间做判断,虽然前端会限制秒杀时间,但是也应该在后端做处理,可以将字符串存库存改为哈希存库存与开始结束时间,在lua脚本中对时间做判断