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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 还是秒杀.秒杀一般有几个场景1.电商秒杀商品2.抢红包3.抢票假设一个场景如下:    某电商公司搞活动,一折秒杀,推出几种秒杀的商品,每种商品1000个,预计100w人抢购要求:    不能超卖.
还是秒杀.
秒杀一般有几个场景
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

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
3月前
|
消息中间件 程序员 Kafka
抢购不再卡顿!揭秘异步处理如何优化秒杀流程!
本文由程序员小米分享,详细介绍了如何通过异步处理简化秒杀请求中的业务流程,提高系统效率与稳定性。主要内容包括秒杀场景的挑战、核心思路、核心业务(生成订单、扣减库存)及次要业务(发放优惠券、增加积分)的异步处理方法,并探讨了使用消息队列的优势及优化用户体验的策略。通过异步处理,系统能更好地应对高并发请求,提升响应速度和稳定性。
68 4
抢购不再卡顿!揭秘异步处理如何优化秒杀流程!
|
5月前
|
消息中间件 缓存 监控
在订单系统中实现高并发的支付处理
在订单系统中实现高并发的支付处理
211 4
|
数据库
.商品秒杀-超卖
.商品秒杀-超卖问题
90 0
|
Java
记录一次简单的秒杀场景
记录一次简单的秒杀场景
69 0
|
缓存 CDN
实现高并发秒杀的7种方式 建议收藏!!
实现高并发秒杀的7种方式 建议收藏!!
135 0
|
Java 测试技术 数据库
实现高并发秒杀的七种方式(2)
实现高并发秒杀的七种方式(2)
|
消息中间件 JavaScript 小程序
实现高并发秒杀的七种方式(1)
实现高并发秒杀的七种方式(1)
|
缓存 监控 NoSQL
秒杀系统
秒杀能够以极小的经费撬动巨大的流量,虽然会带来一定的口碑损失,但因为极具性价比,所以经常被运营同学使用。本文介绍如何设计一款能够支撑60W QPS的秒杀系统,希望能够帮助到大家。
|
SQL 缓存 NoSQL
高并发下秒杀商品,你必须知道的9个细节
高并发下秒杀商品,你必须知道的9个细节
高并发下秒杀商品,你必须知道的9个细节
|
存储 消息中间件 缓存
【高并发】高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!
在电商领域,存在着典型的秒杀业务场景,那何谓秒杀场景呢。简单的来说就是一件商品的购买人数远远大于这件商品的库存,而且这件商品在很短的时间内就会被抢购一空。 比如每年的618、双11大促,小米新品促销等业务场景,就是典型的秒杀业务场景。
899 1
【高并发】高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!