记一次用户抢购商品导致服务器炸了

回复之前的误解

记一次用户抢购商品导致服务器炸了
之前因为没有将 php-fpm 并发配置调高,导致一堆 502

后面调高就好了。

一堆瞎操作,原来是没有调整配置….

最后就 60% cpu

小程序: 狮力健 (现在几乎没什么人了 口罩的高峰期过了)

问题产生

某个商品突然说要做秒杀。
秒杀,挺耗费时间了,老板和客户商量,最后改成抢购。

其实就是增加库存功能,然后人工在固定时间改库存,用户抢购。 (哈哈哈 就是这么简单)

活动规则

记一次服务器抢购导致服务器炸了

昨天 ( 5 - 11)

一次服务器抢购导致服务器炸了

服务器直接炸了,几十秒之内所有项目不能访问。
出现超卖。

在10号才三十几人,没想到11号,马上变成1.2k人,我丢。

我本以为是个小项目,什么限制都没做。
(经历过太多这种小项目了)

11号10点一到,直接炸了,还超卖。

改善

一次服务器抢购导致服务器炸了

限制: (都要先经过 redis 的限制)

  1. 每个人每秒只能访问一次
  2. lpop保证不超卖

我以为这样不会炸了。

今天 ( 5 - 12 )

一次服务器抢购导致服务器炸了

又炸了,但是没有超卖,比昨天好很多,还能访问。
(3秒)

残余问题

少卖

用户发起购买请求,库存-1,返回支付参数给前端。 也就是说: 用户不支付,也会库存-1

本来30个名额的,现在卖了30个,只有20个支付了,10个未支付,所以是少卖了。

我不想用复杂的来写

  1. 老板不会ra我花费太多时间来 客户也没有说什么
  2. 我怕坑,如果用了队列,还要自己测,测了上线还担心bug

我慢慢想 简单 && 又能解决的办法

为何不在支付成功后再库存-1 ?

除了抢购商品都是这样处理的

抢购商品这样处理的话 逗我吗 ……

本作品采用《CC 协议》,转载必须注明作者和本文链接
专心学习不瞎搞
本帖由系统于 5个月前 自动加精
lyxxxh
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 34

如果抢购成功,就直接让其支付。不支付,就视其放弃抢购,然后加回到库存里去。这样可以避免被人恶意占用库存。

5个月前 评论
Summer

没付款的库存就留明天抢购嘛,都一样的

5个月前 评论
dbkuaizi 5个月前
lyxxxh (楼主) 5个月前

可以写个定时任务,30分钟之后未付款的库存在加回去

5个月前 评论

@晓轩 30分钟后可能抢购都结束了,真想抢购的没抢购,抢到的又不付钱,我看限时抢购应该付钱了再减库存。

5个月前 评论
xujinhuan 2个月前
lyxxxh (楼主) 5个月前
轻描淡写 (作者) 5个月前
lyxxxh (楼主) 5个月前
vinhson 5个月前

1、下单减库存。即生成一个订单,库存减1 。这种情况,会出现恶意下单情况,用户只下单,不付款。

2、付款减库存。即订单付完款后才减去相应的库存。这种处理方式不会出现1内的问题,但会出现用户拍下时有货,付款后没货的情况,用户体验极差 :joy:。

5个月前 评论

@一个人的江湖 所以说多备点货才是最好的解决方法 :joy:

5个月前 评论

用 redis 单线程不会出现重复问题,同一个地址 ip 手机号都该认为是同一个用户,限制用户,然后客户端可以加个验签,访问做个限制,防止别人跑脚本

5个月前 评论
bulin 5个月前
xman99 2个月前

5分钟内支付啊,未支付就是放弃资格

5个月前 评论

下单的时候

update goods set stock = stock - 1 where stock - 1 > 0 
5个月前 评论

@crazy 这个语句是不是会剩最后一个商品谁也拍不走吧

5个月前 评论
crazy 5个月前
yidane 3个月前
crazy 3个月前
yidane 3个月前
crazy 3个月前

如果抢购成功,就直接让其支付。不支付,就视其放弃抢购,然后加回到库存里去。这样可以避免被人恶意占用库存。

5个月前 评论

话说没上锁吗,不然怎么会超卖呢,,,

5个月前 评论

几台服务器?简单描述下架构? :grin:

5个月前 评论

1、设定一个较短的时间不付款取消订单,如5分钟

2、在用户下单的时候如果库存为0那么一并返回未付款的人数,合并后返回信息如 “商品已售空, 12人未付款, 请休息一下再抢, 还有机会哦~”

5个月前 评论
lyxxxh (楼主) 5个月前
s51983 (作者) 5个月前

付款成功减少库存,商品库存为1的时候,然后n个人同时付款,最后只有一个人购买成购,还要做自动退款处理 :joy:

5个月前 评论

我们之前遇到过,用秒杀系统容易超卖,后来改成抢优惠券 :sob:

5个月前 评论
犯二青年

我想到的方案有两种:

  1. 在用户下单的时候,加锁,事务开启,限制好库存,生成订单,然后计时5分钟,如果5分钟内用户不支付,则取消该用户的订单,意思是限制下单数量,不支付的回退,支付的一样要走事务加锁,然后修改。
  2. 所有用户可以进行下单,但是在创建秒杀成功的时候需要对应库存,一样事务开启,加锁,意思就是谁先支付就是谁的,我只卖20个。

注意:建议是返回一个秒杀地址,所带参数使用加密算法进行加密(这样可以防止恶意秒杀)

具体实现:

//秒杀逻辑开始
DB::transaction(function ()  {
    $seckill = Seckill::query
        ->lockForUpdate()
        ->findOrFail($id);

    // todo:判断当前用户是否满足秒杀需求

    $user = Auth::guard('api')->user();

   // todo:判断用户是否已秒杀过,判断当前秒杀库存是否足够

    try {
        // 执行用户秒杀逻辑
    } catch (HttpException $httpException) {
        \DB::rollBack();
    };
});
5个月前 评论
zh-mead 5个月前
犯二青年 (作者) 5个月前

面试题常考的,让你碰到了。

5个月前 评论

依稀记得疫情期间我项目抢特价蔬菜一下400份秒空还没有用redis限制,,我的居然没超卖...

5个月前 评论
s51983 5个月前

实际抢购商品的数量和抢购人数相差很大可以直接在前面随机拒绝掉一大波人

5个月前 评论
wanghan 3个月前

我给出的建议是:

1、少卖问题处理: 用户购买生成订单才库存数-1,并返回支付参数给前端,同时设置订单的有效期在1-5分钟之内,如果时间过了库存数+1,因为秒抢的的商品过了5分钟就不会有人抱有希望,所以把握这个点。

2、优化处理:

  • 库存数可以用redis 存储,毕竟redis比mysql快
  • 已支付的订单数等于总库存数时,才告知用户已售完,其他时候可以设置一个等待的动画,例如小米的动画,让用户等待
5个月前 评论

nginx 弄炸的,

5个月前 评论

一样的, 所有的抢购是抢到了就-1, 但是一般都有订单超时自动取消业务的, 你可以使用redis key过期事件来监听订单超时未支付自动取消, 这样把库存加回来

5个月前 评论

我们一般都是把商品信息, 库存都存到redis中, 这样避免频繁查询mysql, 但是会有很小的概率碰到缓存不一致的问题, 只能隔段时间清次redis, 不知道大家缓存不一致的问题是怎么解决的呢?

5个月前 评论
mingzaily 5个月前

【还有3天就不用了,客户没有说必须要改,老板不想改,我不敢改 怕出问题】 老铁在这个公司扮演的是嘛角色 :joy:

4个月前 评论

既然都用了redis就继续用redis来解决呀。发起付款,减库存。来个超时自动将未付款订单设置为超时。同时释放库存呢。

3个月前 评论
如果 php-fpm 架构承受不住流量冲击,可以考虑 Swoole 或者 API 网关来进行流量过滤。
3个月前 评论

比较好奇服务器的配置是什么?

3个月前 评论

秒杀系统设计,推荐这篇文章:博客:秒杀系统的设计

3个月前 评论

淘宝京东都是下单锁库存的,看直播经常有踢人释放库存的。

3个月前 评论

我有个朋友想知道是哪个小程序做特价,顺便提一句,高并发的业务场景少用io操作,比如你用的:config

3个月前 评论
lyxxxh (楼主) 3个月前

和我遇到的情况类似,我是做了时间限制,一分半订单过期不能支付,两分钟后执行定时任务取消该订单,增加库存量

3个月前 评论

学习小米模式-先预约-在发码-在付款 这样就不会有超卖的情况了。。。码都是预先生成和库存一致的

3个月前 评论
Mr_Guo 2个月前
wangchunbo

你都说了小项目了,那就别在乎这点了。。。。

3个月前 评论

我只想说前东家的公司服务器是16核16G的,数据库是4核16G,抢购直接用LockforUpdate都没事

2个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!