目录
秒杀中应用的技术点
1.事务操作:校验库存、扣除库存、创建订单一气呵成
2.乐观锁:防止多线程并发下商品超卖
3.令牌桶算法:实现限流、RabbitMQ、Hystrix、Sentinel均可以实现同样的功能
4.Redis:隐藏秒杀接口、限制用户秒杀频率
5.基于RabbitMQ实现消息的异步处理

秒杀的业务逻辑
首先看数据库
有两张表,一张订单表,一张库存表。库存表中的count字段代表“一开始一共有多少库存”、sale字段代表“已售了多少” 、version用来记录“乐观锁的版本号”

再来看业务
“秒杀”代表这抢购,抢购就先查库存是否有剩余然后创建订单,后面可能还要支付啊,取消订单退回库存等一系列事务操作。
秒杀的代码逻辑
请求进来后先走Controller,在Service层依次校验库存、扣除库存、创建订单。最新持久化到DB中

基于事务的秒杀
我们来看Service,增加事务注解是为了保证多个对数据库的操作同步。
@Service
@Transactional
public class msServiceImpl implements msService {
@Autowired
private StockDAO stockDAO;
@Autowired
private OrderDAO orderDAO;
@Override
public int kill(Integer id){
//根据商品id校验库存
Stock stock = stockDAO.checkStock(id);
if(stock.getSale().equals(stock.getCount())){
throw new RuntimeException("库存不足");
}else {
//扣除库存
stock.setSale(stock.getSale()+1);
stockDAO.updataSale(stock);
//创建订单
Order order = new Order();
order.setSid(stock.getId()).setName(stock.getName()).setCreateDate(new Date());
orderDAO.createOrder(order);
return order.getId();
}
}
}
我们自己测试时,也确实不会有问题。但当我们用Jmeter等测试工具模拟上千并发时,就会出现多线程的问题了,库存和订单记录都会莫名其妙的多上百条,学过多线程的朋友应该知道,这是因为我们没有使用锁操作。
悲观锁解决多线程并发事务问题
我们引入了Sychronized锁,也叫做悲观锁。
在Service的方法上添加synchronized关键字之后,你会发现订单表和库存表的数量仍然对不上。
public synchronized int kill(Integer id){}
这是使用Sychronized的一个大坑。注意:我们在类上添加了事务操作Transactional,又在方法上添加了Sychronized锁机制。程序运行时走的是2个同步,当事务执行完时,此时老的线程由于释放Sychronized锁,新线程拿着sychronized锁来了。 当事务准备提交时,带着刚来的新线程提交了一份,当这个新线程执行完毕后,事务又把它提交了一份。这就造成了数据重复。
因此,如果想要用悲观锁解决线程同步的问题,一定要确保synchronized操作在transactional之前,我们可以把sychronized放在controller中

悲观锁会给一个进程上锁,其他所有进程都处于阻塞状态,浪费了程序性能也降低了用户体验。下一篇我们从乐观锁开始搞起

本文深入探讨秒杀系统的业务及代码逻辑,介绍如何利用事务、乐观锁、令牌桶算法等技术解决高并发下的库存超卖问题,以及通过Redis和RabbitMQ优化系统性能。

被折叠的 条评论
为什么被折叠?



