写点什么

如何设计一个秒杀系统学习笔记

用户头像
escray
关注
发布于: 2020 年 11 月 22 日
如何设计一个秒杀系统学习笔记

开篇词 | 秒杀系统架构设计都有哪些关键点?


秒杀系统要解决两个问题:并发读和并发写。并发读的优化理念是尽量减少用户到服务端来“读”数据,也就是访问数据库;并发写的处理,要求在数据库层面独立出一个库,做特殊处理。


秒杀系统要求稳、准、快。


  • 稳,是说高可用,流量超出预期也不能掉链子,需要有一个兜底方案;

  • 准,保持数据一致性,不能超卖,设计秒杀减库存方案;

  • 快,高性能,数据的动静分离、热点的发现与隔离、请求的削峰与分层过滤、服务端极致优化。


因为在参加极客大学架构师训练营的课程,第九周的内容里面讲到了秒杀系统,所以来这里学习一下。


秒杀系统要解决并发读和并发写两个问题,要达到稳、准、快的要求,这几个关键点总结的很好。


其实这种 7 天的小课,形式挺好的,适合周末或者假期学习,学习下来应该也不累,后来似乎没有了。


01 | 设计秒杀系统时应该注意的 5 个架构原则


秒杀就是在同一个时刻有大量的请求争抢购买同一个商品并完成交易的过程。


秒杀系统的架构原则:数据要尽量少,请求数要尽量少,路径要尽量短,依赖要尽量少,不要有单点。


花了不少时间看留言,其中确实有不少是我之前读正文的时候没有注意到的事情,比如图 2 中的库存热点库,以及两幅图中的箭头。


有一个好奇,为什么从业务上不把秒杀改为抽奖?为什么要秒杀?为了吸引流量么?那么抽奖也可以。


02 | 如何才能做好动静分离?有哪些方案可选?


即使不做秒杀系统,也需要做动静分离,但是秒杀系统可能需要把动静分离做到极致。


动静的差别主要是看数据中是否含有和访问者相关的个性化数据,更具体的说就是看,页面的 HTML 源码中是否含有个性化数据。


文中讲的三种动静分离架构方案,似乎的确有时间的影子,但是即使现在也不是每个秒杀系统都需要上 CDN 的,也可能是大家都被云厂商“惯坏了”。


没有做过秒杀系统,很多内容看的也不是很明白,抱着虚心学习的心态。看到留言里面不少“大佬”吐槽,有点好奇。按照资历和经验来看,国内能够超过作者的人可能并不多,但是似乎都自我感觉良好。


其实专栏的主旨并不是手把手教你搭建一个秒杀系统,而是讲秒杀系统的或者说类似的高并发、高性能、数据一致性的系统的设计思路。


03 | 二八原则:有针对性地处理好系统的“热点数据”


秒杀商品很有可能会成为系统的“热点”,对于那些能够提前预测的静态热点数据,相对比较容易处理(类似于动静分离里面的静态数据),而在系统运行过程中出现的,不能被提前预测到的动态热点数据,无疑会给系统带来更大的“麻烦”。


通常情况下,可能不需要去处理动态热点,而类似于双十一这样持续时间相对长一些,则可能会面临动态热点的问题。


现在比较火的“直播带货”,会带来动态热点么?我感觉一般不会。更可能是静态热点,也就是商家提前会有所准备。


感觉发现动态热点,然后进行处理,这个其实还是挺难的。微博不就一直都没能处理好么,开玩笑的说。


04 | 流量削峰这事应该怎么做?  


看到削峰填谷首先想到消息队列,但是对于秒杀来说,可能时效性的要求会更高一些。


看到很多留言在问如何通过消息队列通知用户,但是我感觉这个应该不难啊,一般可能是用户在秒杀下单页面等待(时间很短),如果消息队列处理结束(成功或者失败),就通知用户。在用户端阻塞,但是在服务端异步。


把老师给 nero 的回答抄录在下面,等着看老师之后的答疑。


“如果是同步的就要等待消息被正确投递后才返回结果,但大部分就是异步的,寄发送后即返回,然后由消息队列保证最后最终被投递,这个要由消息队列自己来承诺 sla”


一开始我以为各种答题只是为了屏蔽机器人或者作弊器,原来还有延缓请求的作用,即使你答题的速度超过 99% 的人,其实也被“延缓”了。


封层过滤比较好理解,真正能进入下单环节(写数据库)的,其实没有多少人。


如果把三种流量削峰的方法结合起来用,那么可能最外层是答题,然后是分层过滤,在后台写系统的时候加入消息队列,不知道这种设想是否正确。


05 | 影响性能的因素有哪些?又该如何提高系统的性能?    


响应时间 RT(Response Time)一般由 CPU 执行时间和线程等待时间组成。正常情况下,响应时间越短,一秒钟的请求数 QPS(Query Per Second)自然也就越高。


总 QPS = (1000ms / 响应时间 RT)× 线程数量


真正对性能有影响的是 CPU 执行时间,线程等待时间对性能的影响不大。我对这一点的理解类似于流水线,假设一个工人每小时能够完成 10 个产品,那么半成品在其工位上的等待时间,与该工位的吞吐量其实没什么影响(半成品可能很多)。


一般情况下,多线程默认配置:线程数 =  2 × CPU 核数 + 1,根据最佳时间得出的公式:


线程数 = [(线程等待时间 + 线程 CPU 时间) / 线程 CPU 时间] × CPU 数量


最好的办法是通过性能测试来发现最佳线程数。


发现短板,考虑物理上的一些制约因素,比如光速、网速、网络结构、TCP/IP、虚拟机和应用本身的瓶颈。


我觉的发现短板或者说是瓶颈,应该是性能优化的第一步。


秒杀系统的瓶颈一般是在 CPU 上,也可能是网络。


优化系统估计可以写一本书,对于秒杀系统而言,主要是:


减少编码,也就是减少字符和字节之间的转换,可以考虑用 resp.getOutputStream() 函数对网页进行直接流输出,还有 velocity 优化,即把静态的字符串提前编码成字节并缓存,然后直接输出字节内容到页面。


减少序列化,也就是避免或者减少 RPC,可以采用合并部署,把两个原本在不同机器上的不同应用,合并部署到一台机器上的同一个 Tomcat 容器中,并且不能走本机的 Socket


Java 极致优化,对大流量的 Web 系统做静态化改造,主要通过直接使用 Servlet 处理请求,以及直接输出流数据两种方式。


并发读优化:采用应用层的 LocalCache,在秒杀系统的单机上缓存商品相关的数据。


06 | 秒杀系统“减库存”设计的核心逻辑


减库存的三种方式:下单减库存、付款减库存、预扣库存,我觉的一般的电商网站,采用下单减库存就可以,相对比较简单。而秒杀系统可能需要预扣库存,但是保留时间可能比较短(比如 1 分钟)。


看到文中提到的恶意下单情况,那么一般的业务系统也需要预扣库存了。而文中推荐秒杀系统采用下单减库存的方式,与我之前的想象,完全相反。


减库存这一篇似乎是整个系列里面留言最多的。


看完了几乎所有留言,@坏坏的举哥 的方案,将库存分为可售库存、未付款库存和未发货库存,看上去的确很好。


07 | 准备 Plan B:如何设计兜底方案?      


Plan B 有两层意思,一个是说在前面所有的方案之外,再叠加一个保险方案;另外就是说,不得不用的方案。


高可用系统建设框架估计也是知易行难的事情,除非做到系统架构师级别,否则也只能是纸上谈兵。不过知道一个大致的高可用框架——架构、编码、测试、发布、运行、故障处理全生命周期,也是有意义的,至少知道整体的蓝图是什么样子的——预防、管控、监控、恢复一个也不能少。


降级、限流、拒绝服务,这个应该是一个逐步递进的兜底方案,到了拒绝服务的时候,一般来说应该不会被击穿了。即使用户体验不好,但是至少系统还活着。


发布于: 2020 年 11 月 22 日阅读数: 73
用户头像

escray

关注

Let's Go 2017.11.19 加入

在学 Elasticsearch 的项目经理

评论

发布
暂无评论
如何设计一个秒杀系统学习笔记