Java 秒杀系统方案优化

Java 秒杀系统方案优化

这几天一直在看这方面的视频教程,今天总算是结束了,秒杀大体流程是很清楚的,但是一落到细节还是挺懵的,总结一下学到的知识点吧,写的不好,还请见谅哈 ⊙ω⊙

一. 表结构的设计:

订单表
商品表
秒杀订单表
秒杀商品表

1. 为什么要这么设计?

原因:假如没有下面这两张秒杀表的存在(自然,就需要向上面两张表中添加关于秒杀的字段),这次我们仅仅是做了一个秒杀的业务,如果以后我们需要扩展优惠、促销等活动,难道还要去修改订单表的结构吗?自然不可取。这是一方面的原因,另外单独拎出来这2张表对并发也有一定的限制。

2. 数据库主键的选择?

数据库主键如何选择,mysql自增?UUID?NO,NO。
mysql自增主键就不要想了,平时都很少用的
UUID呢?互联网中也没有这样用的,很low

真正推荐的是: twitter的雪花算法(snowflake)
链接: Twitter的分布式自增ID算法snowflake

二. 页面级高并发的优化

(前后端分离的,比前两种方法好)
1.页面缓存

过去我们是直接通过数据库查询数据,查询后通过model放到域中,返回给前端,前端进行展示。
现在我们多几步操作,我们在mysql查询之后,通过SpringBoot的themleafViewResovler接口的实现类SpringWebContext来操作,手动进行渲染:thymeleafViewResolver。
链接:Java高并发优化之页面缓存
然后再将这个HTML放入到redis中,再有别的访问时候,直接去redis中取即可。

2.URL缓存

思路和上面大致差不多,这次是增加一个商品id作为redis的key,利用上面的类手动渲染成html页面,再存储到redis中
链接:Java高并发优化之URL缓存

3. 页面静态化

仔细想一想,html可以被浏览器缓存,都被浏览器缓存了,怎么能不快

4. 对象缓存

我们将用户的信息放入到redis中。
弊端:每次修改用户信息的时候还要更新缓存

5.其他方式:CDN优化+静态资源的压缩

三. 业务场景:超买、超卖

1.业务场景一:剩一个库存的时候,两个人同时调用减库存方法容易将库存变成负数。

想一想,当我们判断剩一个库存的时候,2个线程业务逻辑都到了减库存的操作,这时候减的话,岂不是要变成负数了?
修改前的sql:
@Update(“update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId}”)
修改后的sql:
@Update(“update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0”)


多了些什么?一个判断,库存数量大于0。因为mysql有锁的机制,所以线程是串行的,当一个进程将库存修改为0的时候,另一个线程进来的时候,发现stock_count>0了,这时候将不再进行修改,解决了库存变负数问题

2. 业务场景二:一个用户发送了两个秒杀请求,这个请求是同步的,很巧妙的避开了判断秒杀是否成功这个业务。所以最后的结果是生成2条订单、2条秒杀订单

业务时这样的:秒杀成功后,我们向订单表和秒杀订单表写入两条数据
解决办法:我们再秒杀订单表中,将userId和goodsId创建 唯一索引

但凡有两条一样的数据,整体的业务就会回滚,保证了一个人一条秒杀订单

四. 接口优化

思路:减少数据库的访问
1) 系统初始化,把商品库存数量加载到Redis
2) 收到请求,Redis预减库存,库存不足,直接返回,否则进入3)
3) 请求入队,立即返回排队中
4) 请求出队,生成订单,减少库存
5) 客户端沦陷,是否秒杀成功

1. 系统初始化:

系统初始化的时候,直接将商品id和库存数量放入到redis中
//我们的类实现InitializingBean接口,重写afterPropertiesSet方法,将要秒杀的商品库存数量放入redis中

2.redis预减库存

当秒杀请求过来的时候,不走mysql,直接去redis中判断库存的数量,通过redis.decr来实现秒杀成功减少库存

3.通过redis判断重复秒杀

很简单,秒杀成功的时候,我们将userId和商品id组合一个新的redis的key,放入到redis中。在进行重复判断的时候,只需在redis中去判断即可,又减少了连接数据库的操作

4. MQ异步、解耦

下来的操作是,我们生成一个消息对象,将用户和商品id放入到消息对象中,通过mq发送出去。我们只需要直接给用户反馈排队中,不需要我们在当前这个业务中写减库存,建订单的操作。而是交给MQ的监听者去做。

5.MQ监听者执行业务逻辑

MQ的监听队列进行减库存,创建订单的操作。
通过MQ是不是解耦了呢

6. 进一步优化:nginx(横向扩展)

在这里插入图片描述

五.安全优化

1. 秒杀接口地址隐藏

想一想,如果恶意用户提前知道了我们的秒杀接口地址,他是不是就搞破坏呢?
解决:用户在获取秒杀地址的时候,我们在其中增加一个随机参数,将这个随机参数+用户id作为key存储到reis中,在用户进行秒杀的时候,前端将用户这个参数传递过来,我们在去redis中进行验证。

2. 数学公式验证码

这个就是验证码了,就不多说了

3. 接口防刷(对接口做限流)

根本思想就是:在一定时间内,每个用户的访问次数不得超过多少次,超过一定次数后,将不允许进行秒杀
具体的实现是 自定义注解+拦截器实现的
链接:高并发处理之接口限流

最后这个我画的很low:
一个小小low图

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值