A few ideas to achieve seckill

Reprinted: http://blog.itpub.net/29254281/viewspace-1800617/

It’s still a seckill.
There are generally several scenarios
for a seckill 1. E-commerce seckill products
2. Red envelope
grabbing 3. Ticket grabbing

Suppose a scenario is as follows: an
    e-commerce company engages in activities, 10% off seckill, and launches several seckill products, each of which costs 1000 One, it is estimated that 100w people rush to buy
requirements:
    not oversold. Absolutely cannot be sold too much. The
    database must deduct inventory and record order details.

Difficulty analysis
1. Can not be blocked.
Massive requests are like blood clots, all over the body, once When a bottleneck is encountered, the entire blood vessel will be blocked.
Therefore, a large number of user requests must be completed as soon as possible.

2.
A large number of update inventory table sets are updated in a single line of the database. Remaining quantity = remaining quantity -1 where product ID=?
This single-line update, If there is a row lock, it will block other transactions and take up valuable database processing power. For

this scenario, based on a lot of information,
I think we can try a few optimizations about spikes. 1. Web server cluster layer, offloading     massive user spike requests , is essentially a sort, first-come-first-served.     But with so many requests, it is very difficult to fully respond.     Therefore, in a Web server cluster, you can consider offloading traffic. For     example, for every ten requests, nine are randomly discarded, and only Release a request to the subsequent processing link. Change     the sorting mode of spikes to random lottery mode. 2. Web server cluster layer, narrow the scope of locks.









    

    每次秒杀活动开始之前.先计算活动推出的商品数量,然后分配一个限额到每个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 剩余数量>and 
  5. 剩余数量=(select max(剩余数量) from 库存表 where 商品='电视机')
  6. limit 1;
  7. update 库存表 set 剩余数量=剩余数量-1 where 剩余数量>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

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326777840&siteId=291194637