JAVA应用】如何设计一个秒杀/抢券系统?

2021-01-06 18:24发布

7条回答
IT学习
2楼 · 2021-01-07 09:36

对于一个秒杀系统来说,瞬时的大量请求会对后台服务造成冲击,需要保证服务的可用性以及业务的正确性。

设计了一个高并发高可用的系统简要流程架构如下图:

1.将商品(或券)的信息等静态数据放到cdn节点,实现动静分离

2.业务请求和业务处理之间使用MQ对请求进行削峰

3.读写分离:对于逻辑复杂(用户验证,风控管理,行为分析)的系统,可以将读写部署两套服务进行分离

4.使用缓存:

  • 像库存这种信息无法放到静态页面,为了应对大并发读问题,可以在服务端做本地缓存用于读取,一般缓存时间设置为数秒,缓存失效时(最好在失效前一两秒)重新拉取redis中的库存信息。防止超买问题由扣减行为保证,因此即使和缓存不一致,读本地缓存也不会导致超卖,读场景一般允许脏数据情况。

  • 高并发时,大量的mysql操作会有很大性能问题,因此使用redis集群作为缓存进行读取。

5.防止缓存雪崩:

  • 减少缓存渗透:可以在服务中进行布隆过滤,可以将不存在的商品或券访问在服务端过滤掉;或者对缓存中没有的数据key置为null并设置较短的过期时间,优点是简单易操作,缺点要增加大量的null key,增大了与mysql的不一致性,需要消息机制和mysql保持同步

  • 限流:对key的操作加锁排队(利用setnx进行分布式锁),即同时只允许一个线程访问。

  • 防止缓存同时失效:设置key的过期时间时,采用固定时间+随机时间的方法,可以有效防止缓存同时过期引起雪崩的问题

  • 增加二级缓存: A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

6.合理设置redis中的key,使其均匀的分布到各个节点,防止大量请求访问到少量节点的热点问题,而且可以减少单个节点宕机造成的影响。

7.由于商品或券具有独立性, 每个商品或券分配一个独有id,可以根据id范围划分到各个服务集群中,服务在访问对应范围的的redis缓存,这样可以大大增加访问的并发性。实现方式一个是在web服务中将id路由到对应的消息队列topic中,另一个是在消息队列后部署消息路由集群,相对来说方案一更好,因为方案而增加了链路长度和故障点。

8.在7的基础上,为了进一步提升性能防止少量热点数据影响全量数据的操作性能,可在服务中统计热点数据(比如LRU),并将热点数据独立存放到一个集群中去,这样可以将热点数据和其它数据隔离开来。当然这样会增加热点数据迁移的工作,增加了系统的复杂性。


aijingda
3楼 · 2021-01-07 10:05

什么是秒杀?

“秒杀”是商家在特定时间点进行促销的一种运营手段,体现在系统层面,是指一个Web系统,在一秒钟收到数以万计的用户请求,来抢购数量有限的促销产品。本质上,秒杀系统就是一个“三高”系统,即高并发、高性能、高可用的分布式系统。本文主要从“高并发”的角度,来看看需要解决的主要问题。

秒杀需要处理并发读,并发写。“并发读”需要通过各种手段减少用户到服务端来读数据,而“并发写”主要需要保证数据的正确性,确保大流量下数据一致。

秒杀系统关注三方面

下面以一个秒杀系统的例子,来看看一个秒杀系统主要关注的三方面问题。


一、高并发访问

秒杀前用户不断刷新页面查看,来获得购买按钮,抢购时集中点击并发购买,核心是对系统数据的高并发读访问,可以通过如下手段进行防护。

访问拦截:从浏览器/反向代理/Web层/服务层/数据层多层拦截流量,尽量把访问拦截在离用户更近的层,减少对后台的压力;

分流:主要通过分布式集群技术,多台机器处理,提高并发能力;

动静分离:将动态数据和静态数据尽量分离,并通过缓存和CDN等技术对数据读取进行加速;


二、数据正确性

秒杀伴随这一系列业务流程,包括浏览商品、进入抢购页、购买、扣减库存、支付。其中并发写主要与扣减库存有关,要保证数据的正确性,防止超卖发生。大概的解决方法有:

减库存:事务判断,热点商品放到单独的热点库,增加并发锁

热点:对核心热点数据进行实时分析,并在系统中进行优化或隔离

异步化:是指把购买请求的接受和处理异步化。购买请求先放到队列中,这个过程非常高效,返回客户信息。

限流降级:请求限流,控制系统的访问;对服务的下游依赖进行降级,保证核心链路


三、防作弊

抢购还需要保证公平性,方舟如购票插件购买火车票、黄牛党等。在技术角度,大概方法有:

答题:为了增加购买的复杂度;延缓请求。

Cache校验:处理用户购买请求时校验缓存中是否已记录此商品的购买。

秒杀系统设计原则

1、前台请求数尽量少

前台请求会增加浏览器的负担,尽量简化或合并页面大小(CSS/JS,图片等),去掉页面装饰;减少DNCS解析

2、后台数据尽量少

后台数据会增加网络传输,压缩和字符编码,消耗CPU。需要梳理后台依赖的数据和服务,关注序列化/反序列化方式,减少不必要的数据交互

3、调用路径要尽量短

分布式调用错综复杂,需要尽量缩短调用链路;减少第三方依赖,尽量弱依赖,并做好应用分级

4、尽量不要有单点

高可用和稳定性角度,消除单点;服务无状态化,解藕服务状态和机器,如机器配置动态化;有状态的存储,通过冗余备份提高可用性。


灰机带翅膀
4楼 · 2021-01-07 14:20

1.将商品(或券)的信息等静态数据放到cdn节点,实现动静分离

2.业务请求和业务处理之间使用MQ对请求进行削峰

3.读写分离:对于逻辑复杂(用户验证,风控管理,行为分析)的系统,可以将读写部署两套服务进行分离

4.使用缓存:

像库存这种信息无法放到静态页面,为了应对大并发读问题,可以在服务端做本地缓存用于读取,一般缓存时间设置为数秒,缓存失效时(最好在失效前一两秒)重新拉取redis中的库存信息。防止超买问题由扣减行为保证,因此即使和缓存不一致,读本地缓存也不会导致超卖,读场景一般允许脏数据情况。

高并发时,大量的mysql操作会有很大性能问题,因此使用redis集群作为缓存进行读取。

5.防止缓存雪崩:

减少缓存渗透:可以在服务中进行布隆过滤,可以将不存在的商品或券访问在服务端过滤掉;或者对缓存中没有的数据key置为null并设置较短的过期时间,优点是简单易操作,缺点要增加大量的null key,增大了与mysql的不一致性,需要消息机制和mysql保持同步

限流:对key的操作加锁排队(利用setnx进行分布式锁),即同时只允许一个线程访问。

防止缓存同时失效:设置key的过期时间时,采用固定时间+随机时间的方法,可以有效防止缓存同时过期引起雪崩的问题

增加二级缓存: A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

6.合理设置redis中的key,使其均匀的分布到各个节点,防止大量请求访问到少量节点的热点问题,而且可以减少单个节点宕机造成的影响。

7.由于商品或券具有独立性, 每个商品或券分配一个独有id,可以根据id范围划分到各个服务集群中,服务在访问对应范围的的redis缓存,这样可以大大增加访问的并发性。实现方式一个是在web服务中将id路由到对应的消息队列topic中,另一个是在消息队列后部署消息路由集群,相对来说方案一更好,因为方案而增加了链路长度和故障点。

8.在7的基础上,为了进一步提升性能防止少量热点数据影响全量数据的操作性能,可在服务中统计热点数据(比如LRU),并将热点数据独立存放到一个集群中去,这样可以将热点数据和其它数据隔离开来。当然这样会增加热点数据迁移的工作,增加了系统的复杂性。


Kindery
5楼 · 2021-01-07 14:20

高并发的接口/系统有一个共同的特性,那就是”快”。 
在系统其它条件既定的情况下,系统处理请求越快,用户得到反馈的时间就越短,单位时间内服务器能够处理请求的数量就会越多。所以”快”几乎可以算是高并发系统的要满足的必要条件,要评估一个系统性能如何,某次优化是否提高系统的容量,”快”是一个很直观的衡量标准。

py大白
6楼 · 2021-01-08 09:37

什么是秒杀?

“秒杀”是商家在特定时间点进行促销的一种运营手段,体现在系统层面,是指一个Web系统,在一秒钟收到数以万计的用户请求,来抢购数量有限的促销产品。本质上,秒杀系统就是一个“三高”系统,即高并发、高性能、高可用的分布式系统。本文主要从“高并发”的角度,来看看需要解决的主要问题。

秒杀需要处理并发读,并发写。“并发读”需要通过各种手段减少用户到服务端来读数据,而“并发写”主要需要保证数据的正确性,确保大流量下数据一致。

秒杀系统关注三方面

下面以一个秒杀系统的例子,来看看一个秒杀系统主要关注的三方面问题。


一、高并发访问

秒杀前用户不断刷新页面查看,来获得购买按钮,抢购时集中点击并发购买,核心是对系统数据的高并发读访问,可以通过如下手段进行防护。

访问拦截:从浏览器/反向代理/Web层/服务层/数据层多层拦截流量,尽量把访问拦截在离用户更近的层,减少对后台的压力;

分流:主要通过分布式集群技术,多台机器处理,提高并发能力;

动静分离:将动态数据和静态数据尽量分离,并通过缓存和CDN等技术对数据读取进行加速;


二、数据正确性

秒杀伴随这一系列业务流程,包括浏览商品、进入抢购页、购买、扣减库存、支付。其中并发写主要与扣减库存有关,要保证数据的正确性,防止超卖发生。大概的解决方法有:

减库存:事务判断,热点商品放到单独的热点库,增加并发锁

热点:对核心热点数据进行实时分析,并在系统中进行优化或隔离

异步化:是指把购买请求的接受和处理异步化。购买请求先放到队列中,这个过程非常高效,返回客户信息。

限流降级:请求限流,控制系统的访问;对服务的下游依赖进行降级,保证核心链路


三、防作弊

抢购还需要保证公平性,方舟如购票插件购买火车票、黄牛党等。在技术角度,大概方法有:

答题:为了增加购买的复杂度;延缓请求。

Cache校验:处理用户购买请求时校验缓存中是否已记录此商品的购买。

秒杀系统设计原则

1、前台请求数尽量少

前台请求会增加浏览器的负担,尽量简化或合并页面大小(CSS/JS,图片等),去掉页面装饰;减少DNCS解析

2、后台数据尽量少

后台数据会增加网络传输,压缩和字符编码,消耗CPU。需要梳理后台依赖的数据和服务,关注序列化/反序列化方式,减少不必要的数据交互

3、调用路径要尽量短

分布式调用错综复杂,需要尽量缩短调用链路;减少第三方依赖,尽量弱依赖,并做好应用分级

4、尽量不要有单点

高可用和稳定性角度,消除单点;服务无状态化,解藕服务状态和机器,如机器配置动态化;有状态的存储,通过冗余备份提高可用性


秀儿
7楼 · 2021-02-26 14:42

高并发的接口/系统有一个共同的特性,那就是”快”。 
在系统其它条件既定的情况下,系统处理请求越快,用户得到反馈的时间就越短,单位时间内服务器能够处理请求的数量就会越多。所以”快”几乎可以算是高并发系统的要满足的必要条件,要评估一个系统性能如何,某次优化是否提高系统的容量,”快”是一个很直观的衡量标准。

嘿呦嘿呦拔萝卜
8楼 · 2021-03-26 17:24

对于一个秒杀系统来说,瞬时的大量请求会对后台服务造成冲击,需要保证服务的可用性以及业务的正确性。

相关问题推荐

  • 回答 20

    100-199 用于指定客户端应相应的某些动作。 200-299 用于表示请求成功。 300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。 400-499 用于指出客户端的错误。 400 语义有误,当前请求无法被服务器理解。 401 当前请求需要用户验证...

  • 回答 5
    已采纳

    1、相同点(1)都是表现层框架,都是基于MVC设计模型(2)底层都离不开 Servlet API(3)处理请求的机制都是一个核心控制器2、不同点(1)SpringMVC的入口是Servlet,而Struts2的入口是Filter(2)SpringMVC是基于方法设计的,而Struts2是基于类(3)SpringMV...

  • 回答 22
    已采纳

    类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结...

  • 回答 23
    已采纳

    (1)idea启动时会有两个快捷方式,安装完后默认生成在桌面的是32位的idea的快捷方式,如果我们使用这个快捷方式运行大项目,一般都会很卡。解决方法是找到idea的安装目录,然后进入bin文件夹,找到名称为idea64的应用程序,右键他生成桌面快捷方式。以后每次...

  • 回答 12
    已采纳

    获取Map集合中所有的key可以通过map集合的keySet()方法获取例如:    Map map = new HashMap();    map.put(xx,xx); //存放数据    //.... 省略    Set set = map.keySet();    //可以通过迭代器进行测试    Iterator iter = set.iter...

  • 回答 4
    已采纳

    Java中有八种数据类型,基础数据类型分别是:byte,short,int,long,float,double,char,boolean,引用数据类型分别是:数组,类和接口。方法传参的时候我们有两种,一种是形式参数(定义方法时写的参数),一种是实际参数(调用方法时给的具体值)。首先...

  • 回答 15
    已采纳

    现在的架构很多,各种各样的,如高并发架构、异地多活架构、容器化架构、微服务架构、高可用架构、弹性化架构等,还有和这些架构相关的管理型的技术方法,如 DevOps、应用监控、自动化运维、SOA 服务治理、去 IOE 等等,还有很多。分布式架构其实就是分布式系...

  • 回答 10

    1、监控GC的状态使用各种JVM工具,查看当前日志,分析JVM参数的设置,分析堆内存快照和GC日志,根据实际的各区域的内存划分和GC的执行时间,判断是否需要进行优化2、分析结果、判断是否需要优化如果各项参数设置合理,系统没有超时的日志出现,GC频率也不高,...

  • 回答 7
    已采纳

    里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一...

  • 回答 8
    已采纳

    心里有个预期,然后看看是以什么目的进这家企业工作,要是赚钱的话,那就多要点,要是学习的话,可以根据情况要一个能养活自己的价格。

  • 回答 6

    MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以...

  • 回答 6

    学vue应该要先学习javascript 的基础知识和用法。

  • 回答 8

    1、lambda是jdk8的新特性2、使用lambda的前提,必须是一个接口,接口只能有一个抽象方法3、Lambda 表达式的简单例子:// 1. 不需要参数,返回值为 5  () -> 5    // 2. 接收一个参数(数字类型),返回其2倍的值  x -> 2 * x    // 3. 接受2个参数(数...

没有解决我的问题,去提问