秒杀系统怎么设计?( 二 )


秒杀系统怎么设计?

文章插图
 
DNS轮询:从App/Browser入口,对请求进行分类,静态数据直接去CDN获取,同时Nginx进行集群化,通过DNCS轮询 。
Nginx负载均衡:Nginx可以支持10W的并发访问,Nginx支持配置请求的代理策略,把请求路由到多个Web服务器处理 。Nginx支持的负载均衡策略包括:轮询,权重,ip_hash,fair,url_hash等
分布式架构负载策略:Web层调用service,以及service之间的调用,每个service都需要部署多份 。目前最常用的两个框架技术,spring cloud、dubbo、HSF,都采用客户端负载均衡策略,路由到service的不同实例 。
Redis集群:redis单台几十万的QPS,可通过Redis集群,把数据分配到多台服务器上,减轻每台机器的负载 。
MySQL读写分离:一般不对写请求分流,也可通过分布式数据库技术对写请求分流 。对于读请求,一般采用读写分离的策略 。读库应用单独设置索引,提高读性能 。
高并发之“动静分离”
动态页面:根据实时数据渲染的页面,需要调用后台,读取速度慢
静态页面:存储在文件系统的文件,不经常变动,读取速度很
为了提升效率,尽可能静态化,用静态页面,替换动态页面 。例如,商品信息页,商品信息在发布后,保存下商品信息的静态页面;仅访问必要的数据实时数据 。
几个关注点:
1、 静态数据缓存到离用户最近的地方 。比如浏览器、CDN、服务端的 Cache
2、 可以直接缓存 HTTP 连接 。关键是数据中是否含有和访问者相关的个性化数据,比如输出的数据是否和 URL、浏览者、时间、地域相关,以及是否含有 Cookie 等私密数据 。
3、 经常使用Nginx和Web服务器缓存静态文件 。JAVA 系统本身不擅长处理大量连接请求,需要在Nginx和Web侧尽量缓存
4、 动态数据尽量Json化 。减少不必要的数据传输和解析
5、 缓存还需要考虑失效问题/命中率问题/发布更新问题/动静数据合并;以失效为例,一般有三种方式:超时更新,定时更新,通知更新 。
秒杀之“减库存”
减库存一般有如下方式:下单减库存,付款减库存,预扣库存 。
其中,笔者比较推荐“下单减库存”,因为参加秒杀,买到就是赚到,成功下单后却不付款的情况比较少;同时卖家对秒杀商品的库存有严格限制,逻辑上更为简单 。
当然“下单减库存”,要保证库存数据不能为负数 。
一些方法:
1、事务判断,即保证减后库存不能为负数,否则就回滚;
2、直接设置数据库的字段数据为无符号整数,小于零时会直接报错;
3、热点商品放在单独的热点库
4、并发锁:增加并发锁,比如排队等
5、计数器:在Web层或Service层做一个计数器,技术到达阈值,直接返回抢购失败 。
6、按商品路由:把对同一品类商品的抢购路由到同一个service以及DB链接上,减少不必要的锁竞争 。
实践中的一些原则:
1. 将读数据缓存在 Web 端,过滤掉无效的数据读;
2. 对读数据不做强一致性校验;
3. 对写数据做基于时间的分片,过滤过期请求;
4. 对写请求做限流保护,过滤超载请求;
5. 对写数据强一致性校验,仅校验最后有效的数据 。
秒杀之“热点”
秒杀需要对核心热点数据进行实时分析,处理思路可以从业务/系统/数据不同角度进行优化/限制/隔离,比如:
静态热点数据:提前预测 。卖家报名,提前打标
动态热点数据:系统在运行过程中临时产生的热点
也可以增加单独的热点分析组件,总体控制系统业务的热点,并做对应的限制和隔离方法 。
秒杀之“异步”
秒杀系统设计上,经常使用消息队列来缓冲瞬时流量,把同步的请求调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地消费消息 。
秒杀系统怎么设计?

文章插图
 
本质上,就是把一步变成两步,其中增加一些机制来缓冲 。比如在秒杀优惠券抢购,用户抢码(确定用户是否有资格) 和 系统发码(为有资格用户分发优惠券),抢码QPS可能10几万,只需要知道是否有资格;而发码仅针对有资格用户,QPS可能仅仅几千,使用消息队列,可以大大提高效率 。
除了消息队列,也可以使用其他方式排队,比如线程池加锁等待、内存排队算法(如FIFO)、文件排队(如基于 MySQL binlog 的同步机制)


推荐阅读