秒杀系统:同一时刻有大量请求争抢购买同一商品。红包,12306。。。
涉及大量并发读,并发写。要求系统高可用,高性能,一致性。(快准稳)
高性能:
1.数据要尽量少
-
用户请求的数据尽量少,返回给用户的数据尽量少。数据的网络传输需要时间,服务器处理数据的大小影响CPU压缩和编码消耗。
-
尽量少依赖其他系统,RCP的协议转化,序列化占用时间较多。(那就要从主业务中抽离单独的秒杀系统)
2.请求尽量少
-
这里是指只保留主要请求,其他能省则省,能静态就静态。
-
不同的请求不一样,还涉及域名解析。(APP端要注意)
3.路径尽量短,依赖尽量少
-
一次请求跨越多个服务,系统。增加了序列化,socket链接等成本。这好比我们依赖中间件或服务越多,越不安全,因为我们要考虑他们的可用性,不稳定性。
-
能不依赖的不依赖,秒杀的特殊性让我们考虑怎么用最少的服务满足它,只保留强依赖。
4.不要有单点:
-
单点意味着没有备份,分布式系统中是不允许存在单点的。
-
服务的无状态化,可以在任何机器上部署。
不同场景下的架构设计:
第一阶段:最简单也是秒杀的业务主干,商品增加一个定时上架功能(火车票起售了),仅在秒杀开始才能购买,商品库存卖完了就结束了。
第二阶段:
-
请求量加大后,秒杀系统单独出来一个系统,可以针对性的优化,还不影响主要业务流程。
-
这个系统单独部署在一个机器集群中,不会对其他业务机器负载造成影响。
-
对热点数据缓存。
-
增加秒杀答题,防止秒杀器抢单。
第三阶段:
-
对页面进行动静分离,秒杀时不需要刷新整个页面,把页面沙墟的数据降到最少。
-
在服务端对秒杀商品进行本地缓存,不再需要调用依赖系统获取数据,不需要在从缓存服务器获取数据,减少一切能减少的网络IO,也能避免压垮别的系统。
-
增加系统限流保护。
秒杀系统不仅要做系统的优化,也要在业务设计上减缓系统压力;因为单方面做到极致的成本太高,比如12306的动态图片答题。
热点数据放入缓存,做动静分离,都能有效的减少路径和请求数据量。
减库存(重点:不能超卖)
三种方式:
下单减库存:下单就扣,简单精确,直接数据库扣减。
付款减库存:下单后不减,付款才减。会出现下单了确付不了款,被别人买走了。
预扣:下单后扣减,预留15分钟不支付订单自动取消,库存回滚。下单时再次检查订单预扣的库存还有没有,没有再次扣减库存并支付,如果扣减不成功则不能支付。
tips:印象中电影选座,是没下单,但选中座位了就不能被别人选中,这里应该是选座代替了预扣流程。
我认为系统设计是从业务和技术的结合设计,以下有几个问题待思考
1.恶意下单,有竞对过来买你的低价商品,或者只下单不购买。这时一般设置每次下单个数上限,一天内下单取消次数上限,同一个人只能购买一次,等等限制恶意行为,放弃部分请求和减少无效请求都能缓解应用压力。最后加上反作弊更好了。
2.付款减库存,用户体验不好,用户已经下单成功了,付款时却被告知失败,难道我还要再来一遍么?如果有选择我会放弃使用这个产品,没说这么满是因为12306这座大山。。。
3.库存是不是一定不能超?不能的,就要提示库存不足。但如果可以超,那就可以有容错,自己补货。之前做过一个商城的例子,库存是假的,后台有另一个真正的库存系统,放量是个评估值,目的是防止竞对看到我们的真实存量。
大并发秒杀中到底用哪种减库存?
1.万事无绝对,具体事情具体分析。从上面说的来看,可能预扣比另两种严谨一些。但从目前接触的秒杀场景来看,下单减库存好一些。因为大家的购买意愿都很强,取消或不支付的概率低,也就是无效请求很少,这样库存回滚只占少部分。而付款减库存不仅体验不好,还有第三方支付的参与,时间慢,不稳定性高。预扣就多做了一次检查,没有下单减库存快和直接。这也符合我们的路径尽量短,依赖尽量少的原则。
2.能不能在缓存中减库存?如何在数据库中减库存?
简单的减库存逻辑可以用缓存,如果有事务控制,用数据库较好。但innodb行锁会影响其他用这个表的查询操作,即少部分业务影响了大部分业务。这时候把热点数据隔离出去单独数据库可以避免问题,也能和服务的单机器对应。
然后是解决热点数据自己的并发锁问题,锁的问题到哪里都是排队的,无论是应用层排队还是数据库层排队。
3.订单取消补回库存
4.第三方支付结果和减库存的一致性保证
动静分离
动态数据指的是个性化数据,静态是相对的,对热点数据进行本地存储,自然会快。
把数据缓存到离用户最近的地方。服务端缓存,CDN,用户浏览器。
让web服务器如Nginx做缓存更适合
未完待续。。。
热点发现与隔离
流量削峰
队列缓冲、分层过滤
高可用
降级、限流、拒绝服务
性能优化
如何发现瓶颈
影响性能的因素