关于高并发流量的应对可以看我之前写的https://cloud.tencent.com/developer/article/1924076
最简单,最精确的一种,下单时直接通过数据库的事务机制控制商品库存,这样一定不会出现超卖的情况。
缺点:有些人下完单可能不付款,尤其是恶意下单的人是不会真正付款的,那么既有可能出现想买的人买不成,最终活动也过期了,商家也很烦~ 付款后才真正减库存,否则库存一直保留给其他买家。
缺点:因为下单时不会减库存,所以也就可能出现下单数远远超过真正库存数的情况,尤其会发生在做活动的热门商品上。这样一来,就会导致很多买家下单成功但是付不了款,买家的购物体验自然比较差。付款前,系统会校验该订单的库存是否还有保留:如果没有保留,则再次尝试预扣;如果库存不足(也就是预扣失败)则不允许继续付款;如果预扣成功,则完成付款并实际地减去库存。
缺点:这种方案确实可以在一定程度上缓解上面的问题。但是否就彻底解决了呢?其实没有!针对恶意下单这种情况,虽然把有效的付款时间设置为 10 分钟,但是恶意买家完全可以在 10 分钟后再次下单,或者采用一次下单很多件的方式把库存减完。针对这种情况,解决办法还是要结合安全和反作弊的措施来制止。比如给用户打标,恶意用户进入蜜罐(后面会说)无效库存尝试请求
在涉及到库存的商品一般在进入详情页面或者下单页面的时候把库存给查出来,这时候我们可选的商品数量必然要小于这个库存,不要等用户点了无数次下单,一个个尝试库存是否充足,另外呢即使这样,由于秒杀的高并发高流量,依然可能用户拿到的库存大于服务端库存,因此当用户下单的时候,如果因为库存原因下单失败呢,我们就进行前端拿到的库存值的更新,另外如果库存不足了,直接由下单页返回到详情页,前端置灰下单按钮阻止用户的无效请求;风控、网络安全 有时候我们会判断出用户是有问题的,比如某个用户开开团前一个小时开始极其高频的请求数据(1S刷新10次以上或者更高),那这个时候如果我们进行拦截,那他肯定会想各种各样的方法去破解,最后可能又得手了,那么其实我们这里可以诱导防御的方式,比如说有一种叫做蜜罐的技术,可以理解为直接提供一个仿真环境用于引流恶意用户,给他提供一个啥玩意都有的数据环境,有服务器有缓存有数据库,你可以看详情,然后当他进行下单的时候,咱们沙盒仿真环境也让他下单成功,但是呢 他一看订单(查真实库)就啥也没有,就支付不了,用不了咱们真正库存;
再者呢,可以用多重史诗级难度验证码拦击,咱们可以让网关层在给用户打标签为恶意用户之后呢,咱们让他下单的时候就输入验证码,而且给他的是史诗级难度的验证码,干扰线比字母多多了的那种,而且呢还不止让他验证一次,让他验证个两三次,如果这都难不倒他,就算了;库存缓存化,我们可以把库存做到redis里,我们先采用lua(预查redis库存->再预扣redis的方式)->乐观锁扣除mysql库存 不是直接做到接口层面,因为我们是采用是redis预扣库存方式,我们其实不是怕大量流量过来,我们是怕大量流量+大量库存造成了我们redis这时候形同虚设,大量的DML操作在mysql被阻塞,那么这里我们可以进行限流,突破了redis防线的真正扣减mysql操作需要先申请资源,拿到资源再进行下面的操作,其他的进行拦截或者等待,这个操作可以用哨兵来做;并且这里需要做个稍微精准点的限流,比如做到商品层面,每个商品每秒可下单多少多少...这样,因为我们其实不太怕大量DML,而是怕同一行大量DML;
第三种方案: 据我所知目前有些公司会再mysql层面再做一层开发,他会有行锁竞争的时候在行后追加一个队列,把行锁转换为队列,这样其实也可以很大程度上解决性能问题,排队效率必然比并发竞争阻塞要高得多得多(锁竞争情况下 InnoDB 内部的死锁检测,以及 MySQL Server 和 InnoDB 的切换会比较消耗性能);订单定时取消是一个非常常见的需求,尤其是上面说到的下单减库存模式,因为我们有时候会比较担心用户下单了,但是不支付,这时候又锁住了库存,那其他用户就一直没法购买了,所以我们其实就需要进行订单的自动取消功能,避免长期锁住库存让其他人无法购买;
无法在过期的一瞬间即时处理超时订单**的问题
举个例子,比如团购下单接口有个订单15分钟超时取消订单的操作,但是呢我们有时候没有办法一下子处理那么多订单,让他过期,比如有*十万个订单同一时刻过期,不论咋样我们肯定没有办法同时处理完的.但是呢这种超时订单是绝对不能继续让他进行支付的,咋办呢?
我们可以进行**数据的另类同步**,我们可以在任何查出到这个订单的地方都对状态进行两种判断,比如1 订单状态为超时,2 订单状态为下单中,订单下单时间距离现在超过了15min,这两种状态咱们都认定他为超时,这样呢我们就可以做到一定程度上的订单同一时间超时了.所谓,所有人都认为你牛逼,你就真的牛逼了,毫无破绽那么这里我们可以采用异步MQ进行接收回滚,如果上游需要知晓回滚结果,可能会高频查状态那么可以将回滚状态都存入redis
回滚接口我这里优化的比较少
目前就到这里了,后面有空我会再补充一些