zoukankan      html  css  js  c++  java
  • 关于秒杀的系统架构优化思路

    一、问题的提出

    秒杀或抢购活动一般会经过 预约,下单,支付 ,扛不住的地方在于下单,一般会带来2个问题:

    1、高并发

    比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验。

    2、超卖

    任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难题。

    秒杀系统难做的原因:库存只有一份,瞬间大量用户读和写这些数据。

    例如小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万

    二、架构

    常见站点架构如下

    1)浏览器端,最上层,会执行到一些JS代码

    2)站点层,这一层会访问后端数据,返回数据给浏览器

    3)服务层,向上游屏蔽底层数据细节

    4)数据层,最终的库存是存在这里的

    三、优化思路

    1、将请求尽量拦截在上游:传统秒杀系统之所以挂,请求都压倒了后端数据层,数据库读写锁冲突严重,导致响应慢,下单基本不能成功

    2、利用缓存:这是一个典型的读多些少的应用场景,非常适合使用缓存

    四、优化细节

    1 、浏览器层请求拦截

    a)产品层面,用户点击“查询”或者“购票”后,按钮置灰,禁止用户重复提交请求

    b)js层面,限制用户在x秒之内只能提交一次请求

    可以拦截很多无效请求

    2、站点层请求拦截与页面缓存

    防止像服务器直接发送过多的恶意http请求

    a)同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面

    b)同一个item的查询,例如手机车次,做页面缓,x秒内到达站点层的请求,均返回同一页面

    可以拦截很多无效请求

    3、服务层请求拦截与数据缓存

    a)给过多的请求去数据库有什么意义呢?对于写请求,做请求队列,每次只透过有限的写请求去数据层,如果均成功再放下一批,如果库存不够则队列里的写请求全部返回“已售完

    b)对于读请求cache来抗,用memcached or redis(10wqps)

    如此限流,只有非常少的写请求,和非常少的读缓存mis的请求会透到数据层去

    4、数据层闲庭信步

    到了数据这一层,几乎就没有什么请求了,库存是有限的,透过过多请求来数据库没有意义

    五、解决方案

    关于超卖,首先设定一个前提,为了防止超卖现象,所有减库存操作都需要进行一次减后检查,保证减完不能等于负数。(由于MySQL事务的特性,这种方法只能降低超卖的数量,但是不可能完全避免超卖)

    update number set x=x-1 where (x -1 ) >= 0; 

    解决方案1:

    将存库从MySQL前移到Redis中,所有的写操作放到内存中,由于Redis中不存在锁故不会出现互相等待,并且由于Redis的写性能和读性能都远高于MySQL,这就解决了高并发下的性能问题。然后通过队列等异步手段,将变化的数据异步写入到DB中。

    优点:解决性能问题

    缺点:没有解决超卖问题,同时由于异步写入DB,存在某一时刻DB和Redis中数据不一致的风险。

    解决方案2:

    引入队列,然后将所有写DB操作在单队列中排队,完全串行处理。当达到库存阀值的时候就不在消费队列,并关闭购买功能。这就解决了超卖问题。

    优点:解决超卖问题,略微提升性能。

    缺点:性能受限于队列处理机处理性能和DB的写入性能中最短的那个,另外多商品同时抢购的时候需要准备多条队列。

    解决方案3:

    将写操作前移到MC中,同时利用MC的轻量级的锁机制CAS来实现减库存操作。

    优点:读写在内存中,操作性能快,引入轻量级锁之后可以保证同一时刻只有一个写入成功,解决减库存问题。

    缺点:没有实测,基于CAS的特性不知道高并发下是否会出现大量更新失败?不过加锁之后肯定对并发性能会有影响。

    解决方案4:

    将提交操作变成两段式,先申请后确认。然后利用Redis的原子自增操作(相比较MySQL的自增来说没有空洞),同时利用Redis的事务特性来发号,保证拿到小于等于库存阀值的号的人都可以成功提交订单。然后数据异步更新到DB中。

    优点:解决超卖问题,库存读写都在内存中,故同时解决性能问题。

    缺点:由于异步写入DB,可能存在数据不一致。另可能存在少买,也就是如果拿到号的人不真正下订单,可能库存减为0,但是订单数并没有达到库存阀值。

    总结

    扩容+限流+内存缓存+排队

  • 相关阅读:
    记第一场省选
    POJ 2083 Fractal 分形
    CodeForces 605A Sorting Railway Cars 思维
    FZU 1896 神奇的魔法数 dp
    FZU 1893 内存管理 模拟
    FZU 1894 志愿者选拔 单调队列
    FZU 1920 Left Mouse Button 简单搜索
    FZU 2086 餐厅点餐
    poj 2299 Ultra-QuickSort 逆序对模版题
    COMP9313 week4a MapReduce
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/6195788.html
Copyright © 2011-2022 走看看