以前做过一个电商项目,有一个积分商城这个活动,因为商品是免费的,积分也很容易获取,这样就造成了商
品很容易被抢光,为了达到用户体验与不超卖,我们是这样实现的!
1.假如我积分商品中的商品是20件,同时200用户进行抢购,为了达到用户体验与不超卖的情况。这个只是举个例子,可以设置商品是100件,10万人进行抢购。
我是这样实现的:
1 . 同时进来200个请求,我只放100个请求,剩下100个请求,就提示,已经抢购完毕,那么还剩下100个请
求,用户体验高了,处理的请求也少了。商品我们则放到Redis里面,里面只存储20个商品,每次更新,与读取都在Redis,当 i < =0 的时候,就提示商品已经抢购完成。
2 . 同样的例子,不过,我们在上面的方法做了一层优化,就是要用户进行拼图,或者输入数字,这样可以避
免有一些用户通过一些手段去抢购,同时也可以达到减轻并发压力问题。
3 . 通过锁的方式,悲观锁,与数据库并行锁,Redis锁,还有的是线程等待时间,这些方法都是可以的,不过,如果加锁,让一个一个进行抢购,会非常慢,用户体验就不好,下面提供例子。
4 .如果用户量超级大,可以用MQ进行消峰。批量操作。
5 .限流,采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。
本章只列出数据存储解决方式:
SQL,直接更新,不去读取,当更新以后,再去读取商品数量,返回给前端!不要在乎说可能会商品真实数量与数据库不符合,其他网站都是这样做的,比如12306,大家应该都用过。
update tar_commodity set quantity = (quantity - #{quantity}) where id = #{commodityId} and (quantity - #{quantity}) >= 0
实现层lmpl,这里要加事务,更新错误,就回滚
@Transactional
@Override
public void orderAdd(OrderParam param) {
try {
Integer i = commodityMapper.updateOrder(param.getCommodityId(),param.getQuantity());
System.out.println("I是多少: " + i);
if(i <= 0){
System.out.println("库存不足,停止售卖");
return;
}
// 这里反复请求数据,可能会让数据崩溃
Commodity commodity = commodityMapper.selectOne(new QueryWrapper<>());
System.out.println("商品数量是: " + commodity.getQuantity());
System.out.println("售卖中");
}catch (Exception e){
System.out.println("库存不足,停止售卖");
}
}
看测试图:
数据库图:
运行效果图:
数据库效果图:
不懂可以加QQ群: 914084240
择其善者而从之,其不善者而改之