极客时间已完结课程限时免费阅读

01 | 高并发系统:它的通用设计方法是什么?

01 | 高并发系统:它的通用设计方法是什么?-极客时间

01 | 高并发系统:它的通用设计方法是什么?

讲述:唐扬

时长12:49大小11.74M

我们知道,高并发代表着大流量,高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案,从而抵抗巨大流量的冲击,带给用户更好的使用体验。这些方案好似能操纵流量,让流量更加平稳地被系统中的服务和组件处理。
来做个简单的比喻吧。
从古至今,长江和黄河流域水患不断,远古时期大禹曾拓宽河道,清除淤沙让流水更加顺畅;都江堰作为史上最成功的治水案例之一,用引流将岷江之水分流到多个支流中,以分担水流压力;三门峡和葛洲坝通过建造水库将水引入水库先存储起来,然后再想办法把水库中的水缓缓地排出去,以此提高下游的抗洪能力。
而我们在应对高并发大流量时也会采用类似“抵御洪水”的方案,归纳起来共有三种方法。
Scale-out(横向扩展):分而治之是一种常见的高并发系统设计方法,采用分布式部署的方式把流量分流开,让每个服务器都承担一部分并发和流量。
缓存:使用缓存来提高系统的性能,就好比用“拓宽河道”的方式抵抗高并发大流量的冲击。
异步:在某些场景下,未处理完成之前我们可以让请求先返回,在数据准备好之后再通知请求方,这样可以在单位时间内处理更多的请求。
简单介绍了这三种方法之后,我再详细地带你了解一下,这样当你在设计高并发系统时就可以有考虑的方向了。当然了,这三种方法会细化出更多的内容,我会在后面的课程中深入讲解。
首先,我们先来了解第一种方法:Scale-out。

Scale-up vs Scale-out

著名的“摩尔定律”是由 Intel 的创始人之一戈登·摩尔于 1965 年提出的。这个定律提到集成电路上可容纳的晶体管的数量约每隔两年会增加一倍。
后来,Intel 首席执行官大卫·豪斯提出“18 个月”的说法,即预计 18 个月会将芯片的性能提升一倍,这个说法广为流传。
摩尔定律虽然描述的是芯片的发展速度,但我们可以延伸为整体的硬件性能,从 20 世纪后半叶开始,计算机硬件的性能是指数级演进的。
直到现在,摩尔定律依然生效,在半个世纪以来的 CPU 发展过程中,芯片厂商靠着在有限面积上做更小的晶体管的黑科技,大幅度地提升着芯片的性能。从第一代集成电路上只有十几个晶体管,到现在一个芯片上动辄几十亿晶体管的数量,摩尔定律指引着芯片厂商完成了技术上的飞跃。
但是有专家预测,摩尔定律可能在未来几年之内不再生效,原因是目前的芯片技术已经做到了 5nm 级别,在工艺上可以突破的空间不大,可能达不到摩尔定律提到的每 18 个月翻一番的速度了。此时,双核和多核技术的产生拯救了摩尔定律,这些技术的思路是将多个 CPU 核心压在一个芯片上,从而大大提升 CPU 的并行处理能力。
我们在高并发系统设计上也沿用了同样的思路,将类似追逐摩尔定律不断提升 CPU 性能的方案叫做 Scale-up(纵向扩展),把类似 CPU 多核心的方案叫做 Scale-out,这两种思路在实现方式上是完全不同的。
Scale-up 通过购买性能更好的硬件来提升系统的并发处理能力,比方说目前系统 4 核 4G 每秒可以处理 200 次请求,那么如果要处理 400 次请求呢?很简单,我们把机器的硬件提升到 8 核 8G(硬件资源的提升可能不是线性的,这里仅为参考)。
Scale-out 则是另外一个思路,它通过将多个低性能的机器组成一个分布式集群来共同抵御高并发流量的冲击。沿用刚才的例子,我们可以使用两台 4 核 4G 的机器来处理那 400 次请求。
那么什么时候选择 Scale-up,什么时候选择 Scale-out 呢?一般来讲,在我们系统设计初期会考虑使用 Scale-up 的方式,因为这种方案足够简单,所谓能用堆砌硬件解决的问题就用硬件来解决,但是当系统并发超过了单机的极限时,我们就要使用 Scale-out 的方式。
Scale-out 虽然能够突破单机的限制,但也会引入一些复杂问题。比如,如果某个节点出现故障如何保证整体可用性?当多个节点有状态需要同步时如何保证状态信息在不同节点的一致性?如何做到使用方无感知的增加和删除节点?其中每一个问题都涉及很多的知识点,我会在后面的课程中深入地讲解,这里暂时不展开了。
说完了 Scale-out,我们再来看看高并发系统设计的另一种方法:缓存。

使用缓存提升性能

Web 2.0 是缓存的时代,这一点毋庸置疑。缓存遍布在系统设计的每个角落,从操作系统到浏览器,从数据库到消息队列,任何略微复杂的服务和组件中你都可以看到缓存的影子。我们使用缓存的主要作用是提升系统的访问性能,在高并发的场景下就可以支撑更多用户的同时访问。
那么为什么缓存可以大幅度提升系统的性能呢?我们知道数据是放在持久化存储中的,一般的持久化存储都是使用磁盘作为存储介质的,而普通磁盘数据由机械手臂、磁头、转轴、盘片组成,盘片又分为磁道、柱面和扇区,盘片构造图我放在下面了。
盘片是存储介质,每个盘片被划分为多个同心圆,信息都被存储在同心圆之中,这些同心圆就是磁道。在磁盘工作时盘片是在高速旋转的,机械手臂驱动磁头沿着径向移动,在磁道上读取所需要的数据。我们把磁头寻找信息花费的时间叫做寻道时间。
普通磁盘的寻道时间是 10ms 左右,而相比于磁盘寻道花费的时间,CPU 执行指令和内存寻址的时间都是在 ns(纳秒)级别,从千兆网卡上读取数据的时间是在μs(微秒)级别。所以在整个计算机体系中磁盘是最慢的一环,甚至比其它的组件要慢几个数量级。因此我们通常使用以内存作为存储介质的缓存,以此提升性能。
当然,缓存的语义已经丰富了很多,我们可以将任何降低响应时间的中间存储都称为缓存。缓存的思想遍布很多设计领域,比如在操作系统中 CPU 有多级缓存,文件有 Page Cache 缓存,你应该有所了解。

异步处理

异步也是一种常见的高并发设计方法,我们在很多文章和演讲中都能听到这个名词,与之共同出现的还有它的反义词:同步。比如分布式服务框架 Dubbo 中有同步方法调用和异步方法调用,IO 模型中有同步 IO 和异步 IO。
那么什么是同步,什么是异步呢?以方法调用为例,同步调用代表调用方要阻塞等待被调用方法中的逻辑执行完成。这种方式下,当被调用方法响应时间较长时,会造成调用方长久的阻塞,在高并发下会造成整体系统性能下降甚至发生雪崩。
异步调用恰恰相反,调用方不需要等待方法逻辑执行完成就可以返回执行其他的逻辑,在被调用方法执行完毕后再通过回调、事件通知等方式将结果反馈给调用方。
异步调用在大规模高并发系统中被大量使用,比如我们熟知的 12306 网站。当我们订票时,页面会显示系统正在排队,这个提示就代表着系统在异步处理我们的订票请求。在 12306 系统中查询余票、下单和更改余票状态都是比较耗时的操作,可能涉及多个内部系统的互相调用,如果是同步调用就会像 12306 刚刚上线时那样,高峰期永远不可能下单成功。
而采用异步的方式,后端处理时会把请求丢到消息队列中,同时快速响应用户,告诉用户我们正在排队处理,然后释放出资源来处理更多的请求。订票请求处理完之后,再通知用户订票成功或者失败。
处理逻辑后移到异步处理程序中,Web 服务的压力小了,资源占用的少了,自然就能接收更多的用户订票请求,系统承受高并发的能力也就提升了。
既然我们了解了这三种方法,那么是不是意味着在高并发系统设计中,开发一个系统时要把这些方法都用上呢?当然不是,系统的设计是不断演进的。
罗马不是一天建成的,系统的设计也是如此。不同量级的系统有不同的痛点,也就有不同的架构设计的侧重点。如果都按照百万、千万并发来设计系统,电商一律向淘宝看齐,IM 全都学习微信和 QQ,那么这些系统的命运一定是灭亡。
因为淘宝、微信的系统虽然能够解决同时百万、千万人同时在线的需求,但其内部的复杂程度也远非我们能够想象的。盲目地追从只能让我们的架构复杂不堪,最终难以维护。就拿从单体架构往服务化演进来说,淘宝也是在经历了多年的发展后,发现系统整体的扩展能力出现问题时,开始启动服务化改造项目的。
我之前也踩过一些坑,参与的一个创业项目在初始阶段就采用了服务化的架构,但由于当时人力有限,团队技术积累不足,因此在实际项目开发过程中,发现无法驾驭如此复杂的架构,也出现了问题难以定位、系统整体性能下降等多方面的问题,甚至连系统宕机了都很难追查到根本原因,最后不得不把服务做整合,回归到简单的单体架构中。
所以我建议一般系统的演进过程应该遵循下面的思路:
最简单的系统设计满足业务需求和流量现状,选择最熟悉的技术体系。
随着流量的增加和业务的变化修正架构中存在问题的点,如单点问题、横向扩展问题、性能无法满足需求的组件。在这个过程中,选择社区成熟的、团队熟悉的组件帮助我们解决问题,在社区没有合适解决方案的前提下才会自己造轮子。
当对架构的小修小补无法满足需求时,考虑重构、重写等大的调整方式以解决现有的问题。
以淘宝为例,当时在业务从 0 到 1 的阶段是通过购买的方式快速搭建了系统。而后,随着流量的增长,淘宝做了一系列的技术改造来提升高并发处理能力,比如数据库存储引擎从 MyISAM 迁移到 InnoDB,数据库做分库分表,增加缓存,启动中间件研发等。
当这些都无法满足时就考虑对整体架构做大规模重构,比如说著名的“五彩石”项目让淘宝的架构从单体演进为服务化架构。正是通过逐步的技术演进,淘宝才进化出如今承担过亿 QPS 的技术架构。
归根结底一句话:高并发系统的演进应该是循序渐进,以解决系统中存在的问题为目的和驱动力的。

课程小结

在今天的课程中,我带着你了解了高并发系统设计的三种通用方法:Scale-out、缓存和异步。这三种方法可以在做方案设计时灵活地运用,但它不是具体实施的方案,而是三种思想,在实际运用中会千变万化。
就拿 Scale-out 来说,数据库一主多从、分库分表、存储分片都是它的实际应用方案。而我们需要注意的是,在应对高并发大流量的时候,系统是可以通过增加机器来承担流量冲击的,至于要采用什么样的方案还是要具体问题具体分析。

一课一思

高并发系统演进是一个渐进的过程,并非一蹴而就的,那么你在系统演进过程中积累了哪些经验又踩到了哪些坑呢?欢迎在留言区与我一同交流。
最后,感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 54

提建议

上一篇
开篇词 | 为什么你要学习高并发系统设计?
下一篇
02 | 架构分层:我们为什么一定要这么做?
 写留言

精选留言(106)

  • 老男孩
    2019-09-18
    martin fowler好像曾经说过,能使用单体解决的问题,就不要采用分布式。不能为了技术而技术,采用分布式固然可以分流用户请求,提高系统的响应能力,但同样也带来了复杂性。软件开发最终的目的是商业利益。非常赞成老师的观点,罗马城不是一天就建立起来的。架构的工作应该是阶段性,解决阶段性系统的复杂性。如果单体跑的很好,或者通过scale up方式在成本可控的情况能解决就不要想着诗和远方,因为系统内部的进程间调用,肯定比不同物理机的进程之间调用要快。
    展开

    作者回复: 说的真好!

    共 5 条评论
    159
  • 陈靖
    2019-09-18
    之前做的创业项目也是遇到盲目优化的问题,系统最核心的撮合结算服务,刚开始只能100次每秒,后来为了优化到百万级,花了大量时间研究各种方案,做了大量的性能测试,耽误了很长时间推向市场,结果最终优化到了不到一万tps,但后面真正上线的结果可能不到也就100tps,所以真正的需求是市场需求,不是一开始就冲着最牛逼的方案搞,线上的需求远比一开始的预想复杂,没足够的资源和动力,绝对不要折腾,不过时刻准备好可能会出现的瓶颈是必要的,免得半夜宕机,慌得一比
    展开
    共 2 条评论
    58
  • Luciano李鑫
    2019-09-19
    代部落伙伴问个问题:关于秒杀这种场景我想的是用redis的list不就完全可以实现吗。活动开始前,先把要抢的N个商品push到list中。活动开始后,每来一个请求就从list中pop一个商品出去,当list为空后直接返回“已抢光”的响应,由于可秒杀的商品数量并不多,能通过的请求也可以采用同步的方式进行,这样不是既简单又快速。可是我搜了一下网上的方案,都特别的复杂,请问这种方式有什么问题吗?

    作者回复: 这个商品应该是放在单一的redis节点里面的吧,放在多个节点里面不好维护一致性。那么如果在流量或者带宽方面超过单个节点的限制咋办?是否要考虑动态和静态数据的隔离?是否要考虑降低redis的写流量峰值?所以流量大了,自然方案就复杂了~

    共 19 条评论
    40
  • 吕宗胜ZJU
    2019-09-18
    高并发除了横向扩容,缓存和异步化,系统还需要做好保护,比如限流降级,过载保护。甚至高并发的问题更是一个系统性问题,从前端到服务端,从产品设计上都要考虑进来。不过这块就比较业务化了,不是常规的操作
    共 5 条评论
    31
  • 2019-09-18
    1.技术在不断演进,演进的目的和内驱动力是解决当前系统存在的问题,过早过度设计大多只会延误系统的发展。一切都以实际情况和需要出发,一步步优化,一步步演进,个人能力提升也是同样的道理。 2.高并发系统设计通用方法:水平拓展,缓存,异步。这只是指导思想,如何更巧妙的运用才是最具魅力的。

    作者回复: 说的真好!

    17
  • 黑暗浪子
    2019-09-19
    现阶段大多数公司其实都没机会碰到高并发。很多时候我们学习它,只是为了将来有一天要用到时候不会一脸懵。继续学习~期待以后的精彩分享
    16
  • 李冲
    2019-10-17
    有些基础概念的理解是很重要的。也许自己看到那几个词语会认为已经理解了背后的意思,其实不是这样的。专业知识真的会出现不知道自己不知道得情况,也许是翻译的锅,也许是缺少交流,也许是没真的看懂。为了避免这种错误,特地写下自己的理解并结合生活中的例子去宜家买东西来解释。若果理解错了,还请大家指出,以便做到正确的理解。 scale up是把店面建成旗舰店,面积大展区多收银台多服务区人手多。边逛边体验,喜欢的就丢到购物车,逛完去收银台买单去服务台开发票走人,无论哪个环节顾客几乎都不用等。 scale out 就是一家店不够就开多几家,把顾客分流到不同的店里去,尽量让各个分店的体验接近旗舰店。 cache 缓存的理解跟文章不一样,缓存的基础是系统有热点,有少数东西的使用率特别高。对宜家的例子来说缓存就像top10的热卖商品的专用摊位,整个店里有几个关键的路口都有摆上。这些销量占比非常大的产品顾客很容易扔进购物车,而不需要像购买普通商品那样跑到哪层楼哪个区哪个货架才能拿到,这加速了顾客的购买过程。 asyn 异步这个话题有点大。理解估计也有不少偏差,明确的说目前服务器上用的最好的linux的epoll是同步的,可以选择阻塞或者不阻塞。真正的异步IO实现有linux的aio(出来的晚,目前还不好用)和windows的IOCP。对于宜家来讲开纸质发票就是同步的,一堆人排队把小票给服务人员后在服务台等机器出发票。而开电子发票是异步的,顾客直接回家,想开票的时候扫描小票上的二维码填写发票信息,等一段时间邮箱就会提示收到电子发票了。
    展开
    共 2 条评论
    13
  • 逍遥法外
    2019-09-18
    架构设计的过程中要识别每个阶段的复杂度,有针对的做架构设计。避免过度设计带来的成本上升。这个原则和李运华老师的架构课讲的架构设计的原则不谋而合。😄
    14
  • 三年过后
    2019-09-18
    老师讲得很好!不过,还是觉得偏理论较多。例如,讲到踩过很多坑,这些坑没有一些案例说明和后来的解决问题方案。比如,之前负责的支付系统项目,在流量不是很大的情况下,就引入了zk集群(3台)zk集群所在的线上服务,存在一台宕机,整个线上支付都不可用。后面解决:只好切回单体服务

    作者回复: 后面课程会有一些关键知识点的案例介绍的

    共 9 条评论
    12
  • 马留
    2019-09-18
    老师,你把“缓存”比喻成“拓宽河道”,个人觉得是不妥当的,应是建水库更好些。拓宽河道比较类似Scale-out

    作者回复: scale-out可以理解为引流和分流;缓存的作用是提速,拓宽河道的作用也是提速,所以有一些的关联

    共 5 条评论
    11
  • 蓝魔丶
    2019-09-18
    我们公司一开始就采用了微服务架构方案,但在实施过程中却缺乏架构演进和优化过程,在支撑业务的同时也造成了很多运维和技术痛点
    共 1 条评论
    9
  • yuan
    2019-09-24
    老师,消息队列用到缓存,之前没有听说过这个,请问哪种消息队列用到缓存?

    作者回复: 内部实现会用到缓存的思想,Kafka会用到pagecache

    共 2 条评论
    8
  • 鞠小军
    2019-11-07
    总结:高并发设计三种思路 1、横向扩展 2、缓存 3、异步 首先,不能为了设计而设计,不要过度设计。单机满足业务需求就不要分布式,架构不能盲目,架构一定是逐步演进的,而且是随着业务的需求逐步进行的。 三种思路理解要宽泛。比如横向扩展可以是集群,可以是主从分离,也可以是分库分表等等。 缓存呢其实无处不在,比如CPU的一级缓存、二级缓存、三级缓存,操作系统缓存(如windows的page.sys文件),mybatis的一级缓存,二级缓存等等。 异步就是解藕操作,比如将订单成交的消息我需要保证准确性而不是实时性,因此就先放入消息队列中,消息被消费的时候在进行业务的处理,这样就可以提高并发请数量
    展开

    作者回复: 👍

    共 2 条评论
    6
  • MYG
    2019-09-19
    讲得真好!小建议,对于一些关键的术语可不可以像我们讲scale-out一样,同时使用中文和英文呢?比如cache,async,这样可以方便将来同时查阅中英文文献。

    作者回复: 好嘞,以后我会注意的:)

    6
  • 水目沾
    2019-10-19
    架构设计三原则:简单原则、合适原则和演进原则
    5
  • 2019-09-27
    老师,多级缓存中,redis层的好处理,但是Nginx缓存,tomcat本地缓存,都是多台机器分布式部署,该如何及时主动更新

    作者回复: 比如说如果要更新缓存就向一个消息队列里发一个消息,tomcat消费消息后清除本地缓存。不过更多的是等待缓存自己过期,或者定期更新缓存

    4
  • 由莫
    2019-09-18
    把缓存比喻成拓宽河道不合理。从另外的角度来看,拓宽河道就是增加流量的带宽。这跟缓存放在一起说感觉让人对缓存有误解。

    作者回复: 当时考虑的是拓宽河道是让水流更快,和缓存思想比较接近

    共 4 条评论
    4
  • 业余草
    2019-09-18
    微服务不是架构演变的终点! https://mp.weixin.qq.com/s/TAHtAreMkxjWLfW1jSP88w

    作者回复: 架构演变是没有终点的:)

    共 7 条评论
    4
  • 兔2🐰🍃
    2019-09-18
    目前的芯片技术已经到达5nm级别了,最近台积电生产的高通骁龙875,使用的是5nm工艺制造,晶体管密度提升到1.713亿/mm²(这个数据看到时也惊讶到了),比7nm提升70%左右,大概2021年的手机上会普遍起来。

    作者回复: 优秀!这个我确实没有了解很多,感谢提醒:)

    共 4 条评论
    4
  • 2020-04-06
    讨论很激烈,很棒,证明能引起共鸣。 小结一下: 1:技术是服务于业务的,不过技术不行,也是服务不好的,所以,无论个人还是国家都想要更好更先进的技术 2:架构是需要演进的,不是一撮而就的 3:分布式技术就是为了解决大流量高并发的问题的,核心思路,一是水平扩展,二是缓存化,三是异步化 4:高并发问题说通俗点就是流量多,多到一台机器根本扛不住的程度,此时该咋弄?弄咋办?其实没啥法子,一个就是用更牛逼的机器,另一个就是多个机器一起扛,前一种思路根本上是走不通的,因为单机性能和容量即使再强也有极限,没得选啦!就只能用多个机器扛,后面的所有技术都是为了使多个机器像一个机器一样工作,原理需要操作系统考虑的事情,外溢出来交给开发工程师来考虑啦! 5:道理都是相通的,想一想怎么快速收麦子?怎么快速除草?怎么应对洪涝?那高并发系统的问题也是需要这样来思考的
    展开

    作者回复: 👍

    共 2 条评论
    3