如何设计一个秒杀系统,是后台求职的标准问题,可以考出求职者方方面面的知识面,以下为自己对设计秒杀系统的总结和心得。尽可量拓展面试中可能遇到的问题和实际设计中一些关键的问题。
秒杀系统就是电商系统的一种形式,通过设计秒杀模块来应对实际电商平台中最考验系统架构的部分。
秒杀系统特点:业务简单(卖家查询,买家下订单减库存),商品定时上架,时间短,瞬时并发量高。
拼多多App中这种设计很常见,动不动就会蹦出来一个秒杀模块,这就是我们目标需要实现的系统。
核心思路是通过缓存,异步,限流来保证系统的高并发和高可用。
设计框架采用流行的微服务框架dubbo(上一篇有介绍),主要把业务分开实现系统间低耦合。
流程:(1)查询商品;(2)创建订单;(3)扣减库存;(4)更新订单;(5)付款;(6)卖家发货。
设计思路:
将请求拦截在系统上游,降低下游压力
充分利用缓存:利用缓存可极大提高系统读写速度
消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动拉取请求信息进行业务处理
前端方案:
页面静态化:将活动页面上所有可以静态的元素静态化,减少动态元素。
将秒杀活动的静态页面提前刷到CDN节点
禁止重复提交:用户提交之后按钮置灰
使用验证码:防止机器人,爬虫以及分散用户请求
用户限流:在某一时间段只允许用户提交一次请求,比如采取IP限流
后端方案:
采用消息队列缓存请求
利用缓存应对读写请求
数据库加乐观锁防止超卖
业务逻辑分离,微服务框架
测试: Apache JMeter压测工具
注册中心使用zookeeper;
缓存采用redis;
消息队列采用RabbitMQ;
用户请求全部交由Gateway模块处理;
Gateway模块使用RPC的方式调用其他模块提供的服务完成业务处理。
高并发
通过缓存,异步,限流来保证系统的高并发和高可用。
上边我们用的所有策略都是保证系统的高并发和高可用。
1. 同一个账号发出多个请求->可以通过Redis这种内存缓存服务,写入一个标志位,只允许1个请求写成功,结合watch的乐观锁的特性,成功写入的则可以继续参加。
2. 多个账号多个请求(同一IP)->弹出验证码或者直接禁止发送多个请求的IP
超卖问题
由于产品限量,到达临界值时由于多线程很难控制,很容易出现请求完成数大于限量商品数,而且都通过余量判断的情况。这就发生了超卖问题。
以下提供三种思路:
悲观锁:在修改数据时采用锁定状态,排斥修改。缺点是可能让请求永远的不到锁,死在那里。
FIFO队列:强行把多线程改成单线程 缺点高峰段相应下降
乐观锁:版本更新,符合版本号的才能成功,比如zk的watch,redis的过期时间;缺点是会增大CPU开销
其他改进数据库方面加唯一索引:防止用户重复购买。
Redis有什么用
1. 分布式session
在用户登录成功之后,将用户信息存储在redis中,然后生成一个token返回给客户端,这个token为存储在redis中的用户信息的key,这样,当客户端第二次访问服务端时会携带token,首先到redis中获取查询该token对应的用户使用是否存在,这样也就不用每次到数据库中去查询是不是该用户了,从而减轻数据库的访问压力
2. 缓存写操作,减少并发压力
存储秒杀地址和订单信息。在做秒杀时,需要先查询数据库中的商品库存,确保逻辑正确,我们将库存信息信息存储在redis中,从而可以减少对数据库的访问。
3. 缓存动态资源,减少数据库压力
存储商品列表页面,减少对数据库列表信息的访问。
系统如何再优化
使用内存标记减少redis访问
秒杀接口地址不写到客户端
在秒杀开始之前,秒杀接口地址不要写到客户端,而是在秒杀开始之后,将秒杀地址动态地在客户端和服务器间进行交互完成拼接。这样一来,秒杀开始之前,秒杀地址对客户端不可见。
实现思路:秒杀开始之前,先去请求接口获取秒杀地址;接口改造,带上@pathVariable参数; 添加生成地址的接口;秒杀收到请求,先验证@pathVariable参数。
使用Nginx水平扩展
REST架构风格,事件驱动设计,全异步网络IO,善于处理高并发事务。
要学会如何介绍好一个系统,首先要知道这个系统的基础知识,然后根据框架图分部分勾勒出整个系统的概况。再结合提问的问题回答所要回答的问题。当然,回答问题的深度取决于你理解技术的深度。比如redis的作用做缓存,要先总体说redis是什么,举出项目中的实例,然后技术上讲redis在工程里可以起到什么样的作用。
测试模板:https://www.cnblogs.com/111testing/p/11437221.html