优极限【完整项目实战】半天带你用-springBoot、Redis轻松实现Jav

秒杀功能设计:
- 秒杀起止时间与动态倒计时、商品原价与秒杀价、商品秒杀库存、“秒杀”按钮的置灰与秒杀进行中的设计
- 秒杀的两个判断:
- 库存够不够(DB中去查而不是相信前端)
- 用户仅限购一件,根据t_seckill_order表的user_id和goods_id两个属性是否重复从而判断他是否有超购行为
- 注意秒杀商品扣减库存时,要同时生成订单和秒杀订单,因为秒杀订单表的订单id和订单表的id是外键关联的
引入Jmeter秒杀接口性能:
028_JMeter简单使用 P28 - 07:00
QPS:Query/s,比如:对一个页面访问的请求数
TPS:Transaction/s,比如:请求-查询-响应三个阶段,tps就是3
- 不同的用户访问商品列表接口
- 不同的用户访问秒杀接口(这是要更新DB的!!)(发生的问题:慢、商品超卖)
压测后秒杀接口性能优化:
- 页面缓存:添加读写多更新少数据的缓存
- Redis、商品列表页面、rul缓存本质还是页面缓存
- 如果商品量足够大,几百几千页,页面缓存可能也就是几十页甚至几页
- 优化效果:较优化前qps提升了1倍
- 对象缓存
- 处理分布式session时,用redis缓存了用户信息,根据cookie获取user,而user信息往往不会更改。即使更改也是极少部分,且为了保证用户数据信息DB和缓存的一致性,可以延迟双删策略
- 页面静态化(拆分页面)
- 商品列表页面
- 秒杀页面
- 订单详情页面
- 静态资源的优化
- CDN优化
- 库存超卖
- 扣减库存前肯定是要根据用户id和商品id去查商品数够不够,那么可将两个id作为联合唯一索引,用以防止一个用户多次抢购的问题
- 细小优化:将秒杀订单信息放到缓存里便于处理(总的性能提升2倍,主要是因为库存卖完后,库存信息放到redis中就不更新了,用户来抢购时就只走redis而不走DB)
- 优化过程中仍然出现的问题:10件秒杀商品库存没有超卖,但是订单却生成了90个(SQL解决的:库存减一+条件库存>0)
044_接口优化方向 P44 - 04:33
优化总结:
- 引入缓存
- 3个页面缓存
- 对象缓存
- 3个页面静态化
- 重点解决超卖问题:
- 加索引防止同一用户多次购买
- sql:update table set stock=stock-1 where stock>0
- 高并发情形下再次优化--接口优化:
- redis预减库存,减少对DB的访问
- 即使redis预减库存,DB仍要交互,高并发情形下咋办?优化下单可用Rabbit MQ队列,缓冲与异步下单
- redis自己也是服务器,单独部署仍有优化空间:内存标记
- 整体优化思路:先将秒杀商品信息存放到redis里,用户点击秒杀后,判断有库存则给用户响应“订单排队中”,并将此扣减库存的请求放到Rabbit MQ里,异步生成订单,出单成功后通过轮询方式给响应用户下单成功或失败。(分库分表、DB集群在适当时也可考虑)
- 加载库存
- 预减库存
- 库存不足直接返回;库存足够则封装请求到MQ
- 后端返回排队中
- 消息出队进行真实扣减库存
- 将成功或失败的消息通过MQ返回给用户
052_Redis预减库存 P52 - 05:55
- 实现初始化Bean时将DB数据加载到Redis中,实现InitializingBean接口,重新afterPropertiesSet方法;预减库存时,为了保证原子性,用了redis的decrement方法,加上库存数量大于0的逻辑可以使得高并发情况下库存不会扣减为负数。(这里也可以用redis锁,异常不能删锁--->配置超时时间;会出现连环删锁怎么办?--->多个命令一次执行---lua脚本,两种实现方式你选哪个?)面试可以说说
- 内存标记、前端轮询获取数据
- redis分布式锁优化项目:lua不放客户端会大幅降低qps
- ThreadLocal
- 主流企业解决方案:
- 抢购预约
- 网关限流
- 分布式锁 性能不太行