如何解决高并发问题(秒杀系统)

优化的方向:

    将请求尽量拦截在系统上游(不要让锁冲突落到数据库上去) 充分利用缓存,大部分是读多写少的场景

常见秒杀架构

  1. 浏览器端 ,最上层,会执行到一些JS代码
  2. 站点层 ,这一层会访问后端数据,拼html页面返回给浏览器
  3. 服务层 ,向上游屏蔽底层数据细节,提供数据访问
  4. 数据层 ,最终的库存是存在这里的,mysql是一个典型(当然还有会缓存)

各层次优化细节

    客户端(浏览器层,APP层)

静态化页面(采用html,不用jsp),将页面缓存在用户的浏览器和CDN上 前端限流: 在用户点击按钮后,对按钮置灰处理,禁止用户重复提交请求;限制用户在X秒之内只能提交一次请求。

    站点层 如何防止程序员循环调用http接口,发送请求? 系统一般都需要登录,对uid进行请求计数和去重。一个uid,x秒只准透过1个请求,剩余的请求用页面缓存,x秒内到达站点层的请求,均返回同一页面。 但是程序发起请求怎么都比人工点击速度快,所以要避免这种情况的话,可以把URL动态化,通过MD5之类的加密算法加密随机的字符串去做url,然后通过前端代码获取url后台校验才能通过。 服务层

做写请求队列控制流量,做数据缓存; 后端限流:没库存了,return个false,前端结束秒杀,后端关闭后续无效请求的介入。 活动开始前,通过定时任务提前把商品的库存加载到Redis中,让整个流程都在Redis里面去做,然后等秒杀介绍了,再异步的去修改库存就好了。 对于采用主从方式的Redis,如何解决高并发? 用Lua脚本,类似Redis事务,有一定的原子性,不会被其他命令插队,可以完成一些Redis事务性的操作。 数据库层操作sql语句加上乐观锁做一个保护。

减库存实现逻辑:

  1. 将商品的开始时间放置在redis缓存中,判断是否到了秒杀开始时间
  2. 请求间隔是否符合正常时间(如将用户上一次的请求时间记录下来计算时间差)
  3. 将商品库存剩余数量放置到redis缓存中,判断库存数量是否还有剩余
  4. 有剩余的话,获取一个redis分布式锁 利用redis的.setnx命令,先判断是否存在再赋值 因为redis都是串行操作的,不存在并发问题 拿到锁:
  5. 处理业务逻辑,如将数据放置在一个mq中,然后通过incr、incrby、decr、decrby原子操作命令控制库存数,减少数据库IO的开销 incr递增1并返回递增后的结果; incrby根据指定值做递增或递减操作并返回递增或递减后的结果(incrby递增或递减取决于传入值的正负); decr递减1并返回递减后的结果; decrby根据指定值做递增或递减操作并返回递增或递减后的结果(decrby递增或递减取决于传入值的正负);
  6. 释放锁,返回应答 未拿到锁:
  7. 线程sleep再次尝试拿锁 ,多次未拿到锁则返回用户活动太火爆

其他优化方式

服务单一职责: 微服务的设计思想和分布式的部署方式,给秒杀也开个服务,单独建库。 这样的好处是:秒杀系统挂了也不会影响其他服务。

Redis集群: 单机Redis顶不住就多弄几个。Redis集群,主从同步、读写分离。

负载均衡: Nginx是高性能的web服务器,并发也能顶几万,而Tomcat只能顶几百并发。 进行负载均衡,多搞点服务器。

秒杀大致流程图

经验分享 程序员 微信小程序 职场和发展