原文出自:公众号 sowhat14121 秒杀场景秒杀场景
原文链接:
https://mp.weixin.qq.com/s/rbXrhzIJG2NtYt_61OmzTA
- 登陆12306进行火车票抢座
- 1599元购入飞天茅台
- 周董演唱会的门票
- 双十一秒杀活动
- 严格防止超卖:库存1000件卖了1020件 , 要杀个码农祭天了!防止超卖是秒杀系统设计最核心的部分 。
- 防止黑产:防止不怀好意的羊毛党薅羊毛 。
- 保证用户体验:高并发下 , 给用户提供友善的购物体验 , 尽可能支持比较高的QPS等等 。
2 第1版-裸奔
文章插图
裸奔秒杀
不加思考 , 上来直接按照 SpringBoot + MyBatis 模式进行秒杀系统的设计 , 流程如下:
- Controller层获得用户秒杀请求后调用Service层 。
- Service层获得请求后要要检查已售数据跟库存总量是否一致 , 一致说明商品卖没了 , 不一致说明还有库存 , 那就调用DAO层对已售数量进行加1 。
- DAO层获得请求后直接通过MyBatis操作数据库实现已售数量加1跟订单创建 。
- A用户进行描述请求 , 此时调用到了Service层 , 发现已售不等于库存 , 此时拿到库存数是64 , A将库存更新为63 , 然后创建订单 。
- B用户进行描述请求 , 此时调用到了Service层 , 发现已售不等于库存 , 此时拿到库存数是64 , B将库存更新为63 , 然后创建订单 。
- 此时库存减少了1个但是订单创建多个 , 卖超了!
无锁并发请求 , 卖超了
文章插图
syn悲观锁
遇见 并发问题 很容易想到以前学过并发编程嘛 , 既然Controller默认是单例模式 , 那我用 synchronized 将Controller层调用Service层的代码进行加锁同步即可 。
这样就可以解决卖超问题了 , 但是须知 , 既然是悲观锁 , 如果有1000个并发请求 , 那只有1个拿到锁了 。有999个会去竞争这个锁的 。
@Transactional
@Service
@Transactional
@Slf4j
public class OrderServiceImpl implements OrderService
{
//校验库存
Stock stock = checkStock(id);
//更新库存
updateSale(stock);
//创建订单
return createOrder(stock);
}
当然了你也可以用Spring自带的事务注解来实现悲观锁的操作 , 因为用了@Transactional就可以实现通过事务来控制 , 要么全部成功 , 要么全部失败 , 用事务时有两点需注意:
- 尽可能将MySQL执行语句往方法体后面靠 , 因为MySQL事务的commit语句是在第一次执行MySQL相关语句开始 , 一直到方法的结束 。
- 设置事务的超时时间 , 如果不设置默认是-1是无限长 。并且事务中设置的耗时timeout = 最后一个MySQL语句耗时 + 以及最后一个MySQL之前的所有耗时 。
4 第3版-乐观锁
文章插图
MySQL版本号
我们为每个数量的已售数据配备个版本号 , 在Service层调用时获得用户的已售数跟对应版本号 , 然后更新时将已售数跟版本号同时更新 。因为 MySQL在更新时会自带乐观加速机制 , 如果更新成功则表示抢购成功 , 更新失败则表示抢购失败 , 此时你会发现不是手速越快就一定能抢到的哦 , 但起码保证了不会超卖 ,
推荐阅读
- 华硕|锐龙7 5800H标压+RTX 3050独显+2.8K OLED:华硕灵耀Pro14轻薄本5999元秒杀
- MySQL多表查询讲解
- 二年级雷雨课文讲解?二年级课文雷雨的文本解读
- 这应该是全网讲解JAVA 异常处理最全的文章了
- Zookeeper的选举算法和脑裂问题深度讲解
- 使用Redis轻松实现秒杀系统
- 全网最硬核解读计算机启动原理
- 建议收藏 一文深度讲解JVM 内存分析工具 MAT及实践
- VSCode安装和测试
- 黑客必用神器“msf”的post模块实战讲解