转载

实现秒杀的几个想法(续)

还是秒杀.
秒杀一般有几个场景
1.电商秒杀商品
2.抢红包
3.抢票

假设一个场景如下:
    某电商公司搞活动,一折秒杀,推出几种秒杀的商品,每种商品1000个,预计100w人抢购
要求:
    不能超卖.绝对不可以卖多了.
    数据库要扣减库存,并且记录订单明细.

难点分析
1.不能阻塞.
海量的请求就像血栓一样,遍走周身,一旦遇到瓶颈,就会堵塞整个血管.
所以一定要让海量的用户请求,尽快结束.

2.数据库单行更新
大量的 update 库存表 set 剩余数量=剩余数量-1 where 商品ID=?
这种单行更新,有行锁,会阻塞其他事务,占用宝贵的数据库处理能力.

针对这种场景,综合了很多资料
我觉得可以尝试几个关于秒杀的优化.

实现秒杀的几个想法(续)

1.Web服务器集群层,卸载流量
    海量的用户秒杀请求,本质上是一个排序,先到先得.
    但是如此之多的请求,完全响应,难度又很大.
    所以在Web服务器集群,可以考虑卸载流量.
    比如每十个请求,随机抛弃九个,只放行一个请求到后续处理环节.
    把秒杀的排序模式,变为随机抽奖的模式.
    
2.Web服务器集群层,缩小锁范围.
    每次秒杀活动开始之前.先计算活动推出的商品数量,然后分配一个限额到每个Web服务器.
    比如一个活动推出秒杀商品
    电视,手机,衣服各1000件,那么每台服务器的限额就是125件.
    将这个限额写入ZooKeeper,Web服务器监听到限额的变化,就会重新初始化各自的商品剩余数量.

模拟示例:
  1.     private static ConcurrentHashMap<String,Integer> map=new ConcurrentHashMap<String, Integer>();
  2.     
  3.     private void zooKeeperHandle(){
  4.         //将ZooKeeper的变化,初始化到Web服务器全局容器
  5.         map.put("电视机", 125);
  6.         map.put("手机", 125);
  7.         map.put("衣服", 125);
  8.     }
    假设用户请求秒杀电视机,它只是锁了该Web服务器电视机的数量。(该Web服务器手机和衣服还可以继续并发处理,当然其他的Web服务器也在同时处理电视机的秒杀请求)
    这样缩小了锁定的范围,增加了系统处理的吞吐量.

    如果这个剩余数量大于零,则将用户ID放入电视机购买队列,然后告知用户秒杀成功
    如果这个剩余数量等于零,则告知用户秒杀失败.即便别的Web服务器还有电视机的剩余配额.

3.ZooKeeper层,ZooKeeper变更库存信息
    假设活动期间,需要修改库存信息。
    两种可能,
    第一种,该商品已经卖了500件,电商不想继续卖了.
    第二种,从仓库中又找到了一些积压库存..

    两种情况,都直接修改ZooKeeper中相应商品的配额.
    Web服务器会监听变化,并重新初始化全局容器.

4.消息队列层,多消费者处理
    消费者主要是从队列获取购买请求,发送至数据库
    扣减数据库库存
    写订单明细记录
实现秒杀的几个想法(续)

5.数据库层,使用存储过程代替JDBC调用
    由于使用了多消费者处理同一队列,增加吞吐量,避免队列堆积过大.
    但是多消费者,必然导致数据库出现单行更新问题.

    单行更新问题就是多个线程,并发修改同一条记录,导致事务相互阻塞.浪费了数据库宝贵的处理能力.
    考查下图.
    假设消费者到数据库的网络是1毫秒
    那么相对于存储过程,使用JDBC的方式,每个事务将至少多持有行锁2毫秒.

    所以进一步优化,可以考虑用存储过程代替JDBC
实现秒杀的几个想法(续)

6.数据库层,库存单行更新,增加多个槽位.
    单行更新场景
实现秒杀的几个想法(续)
    增加槽位的表结构
实现秒杀的几个想法(续)
   
    使用槽位分散行锁
    每种商品的库存,由4个槽位组成.
    事务开始,首先找到剩余数量最多的那个商品槽位.
    然后扣减该槽位的库存.
    这样一个行锁,可以变为4个行锁,系统吞吐量增加了4倍.
    (其实如果update的影响行数为0,表示该槽位已经没有库存.可以重复执行这个过程,再另选一个槽位)

  1. //开始事务
  2. select 商品,剩余数量,@槽位:=Slot from 库存表
  3. where
  4. 商品='电视机' and 剩余数量>0 and
  5. 剩余数量=(select max(剩余数量) from 库存表 where 商品='电视机')
  6. limit 1;

  7. update 库存表 set 剩余数量=剩余数量-1 where 剩余数量>0 and 商品='电视机' and Slot=@槽位;
 //如果update的影响行数不为0,写订单明细表
commit
;

7.数据库层,冷热商品分开.
    某些热点商品,可以单独放置在一个数据库处理
    比如苹果手机新品,特卖打折 10w部
    这种注定会热的商品,应该使用单独的数据库处理
    避免和普通商品竞争,堵塞本次活动其他商品的处理.

参考:
http://blog.itpub.net/29254281/viewspace-1783043/
    

http://jiagou.baijia.baidu.com/article/108134?qq-pf-to=pcqq.group

http://www.infoq.com/cn/presentations/seckill-solution-based-sql

正文到此结束
Loading...