亚马逊大佬带你理解高并发
Editor's Note
给大家推荐一篇 前亚马逊员工 现大厂总监关于高并发的理解
The following article is from 武哥漫谈IT Author 骆俊武
高并发,几乎是每个程序员都想拥有的经验。原因很简单:随着流量变大,会遇到各种各样的技术问题,比如接口响应超时、CPU load升高、GC频繁、死锁、大数据量存储等等,这些问题能推动我们在技术深度上不断精进。
在过往的面试中,如果候选人做过高并发的项目,我通常会让对方谈谈对于高并发的理解,但是能系统性地回答好此问题的人并不多,大概分成这样几类:
1、对数据化的指标没有概念:不清楚选择什么样的指标来衡量高并发系统?分不清并发量和QPS,甚至不知道自己系统的总用户量、活跃用户量,平峰和高峰时的QPS和TPS等关键数据。
2、设计了一些方案,但是细节掌握不透彻:讲不出该方案要关注的技术点和可能带来的副作用。比如读性能有瓶颈会引入缓存,但是忽视了缓存命中率、热点key、数据一致性等问题。
3、理解片面,把高并发设计等同于性能优化:大谈并发编程、多级缓存、异步化、水平扩容,却忽视高可用设计、服务治理和运维保障。
4、掌握大方案,却忽视最基本的东西:能讲清楚垂直分层、水平分区、缓存等大思路,却没意识去分析数据结构是否合理,算法是否高效,没想过从最根本的IO和计算两个维度去做细节优化。
这篇文章,我想结合自己的高并发项目经验,系统性地总结下高并发需要掌握的知识和实践思路,希望对你有所帮助。内容分成以下3个部分:
如何理解高并发? 高并发系统设计的目标是什么? 高并发的实践方案有哪些?
高并发意味着大流量,需要运用技术手段抵抗流量的冲击,这些手段好比操作流量,能让流量更平稳地被系统所处理,带给用户更好的体验。
我们常见的高并发场景有:淘宝的双11、春运时的抢票、微博大V的热点新闻等。除了这些典型事情,每秒几十万请求的秒杀系统、每天千万级的订单系统、每天亿级日活的信息流系统等,都可以归为高并发。
很显然,上面谈到的高并发场景,并发量各不相同,那到底多大并发才算高并发呢?
1、不能只看数字,要看具体的业务场景。不能说10W QPS的秒杀是高并发,而1W QPS的信息流就不是高并发。信息流场景涉及复杂的推荐模型和各种人工策略,它的业务逻辑可能比秒杀场景复杂10倍不止。因此,不在同一个维度,没有任何比较意义。
2、业务都是从0到1做起来的,并发量和QPS只是参考指标,最重要的是:在业务量逐渐变成原来的10倍、100倍的过程中,你是否用到了高并发的处理方法去演进你的系统,从架构设计、编码实现、甚至产品方案等维度去预防和解决高并发引起的问题?而不是一味的升级硬件、加机器做水平扩展。
此外,各个高并发场景的业务特点完全不同:有读多写少的信息流场景、有读多写多的交易场景,那是否有通用的技术方案解决不同场景的高并发问题呢?
我觉得大的思路可以借鉴,别人的方案也可以参考,但是真正落地过程中,细节上还会有无数的坑。另外,由于软硬件环境、技术栈、以及产品逻辑都没法做到完全一致,这些都会导致同样的业务场景,就算用相同的技术方案也会面临不同的问题,这些坑还得一个个趟。
因此,这篇文章我会将重点放在基础知识、通用思路、和我曾经实践过的有效经验上,希望让你对高并发有更深的理解。
先搞清楚高并发系统设计的目标,在此基础上再讨论设计方案和实践经验才有意义和针对性。
高并发绝不意味着只追求高性能,这是很多人片面的理解。从宏观角度看,高并发系统设计的目标有三个:高性能、高可用,以及高可扩展。
1、高性能:性能体现了系统的并行处理能力,在有限的硬件投入下,提高性能意味着节省成本。同时,性能也反映了用户体验,响应时间分别是100毫秒和1秒,给用户的感受是完全不同的。
2、高可用:表示系统可以正常服务的时间。一个全年不停机、无故障;另一个隔三差五出线上事故、宕机,用户肯定选择前者。另外,如果系统只能做到90%可用,也会大大拖累业务。
3、高扩展:表示系统的扩展能力,流量高峰时能否在短时间内完成扩容,更平稳地承接峰值流量,比如双11活动、明星离婚等热点事件。
这3个目标是需要通盘考虑的,因为它们互相关联、甚至也会相互影响。
比如说:考虑系统的扩展能力,你会将服务设计成无状态的,这种集群设计保证了高扩展性,其实也间接提升了系统的性能和可用性。
再比如说:为了保证可用性,通常会对服务接口进行超时设置,以防大量线程阻塞在慢请求上造成系统雪崩,那超时时间设置成多少合理呢?一般,我们会参考依赖服务的性能表现进行设置。
再从微观角度来看,高性能、高可用和高扩展又有哪些具体的指标来衡量?为什么会选择这些指标呢?
❇ 性能指标
通过性能指标可以度量目前存在的性能问题,同时作为性能优化的评估依据。一般来说,会采用一段时间内的接口响应时间作为指标。
1、平均响应时间:最常用,但是缺陷很明显,对于慢请求不敏感。比如1万次请求,其中9900次是1ms,100次是100ms,则平均响应时间为1.99ms,虽然平均耗时仅增加了0.99ms,但是1%请求的响应时间已经增加了100倍。
2、TP90、TP99等分位值:将响应时间按照从小到大排序,TP90表示排在第90分位的响应时间, 分位值越大,对慢请求越敏感。
3、吞吐量:和响应时间呈反比,比如响应时间是1ms,则吞吐量为每秒1000次。
通常,设定性能目标时会兼顾吞吐量和响应时间,比如这样表述:在每秒1万次请求下,AVG控制在50ms以下,TP99控制在100ms以下。对于高并发系统,AVG和TP分位值必须同时要考虑。
另外,从用户体验角度来看,200毫秒被认为是第一个分界点,用户感觉不到延迟,1秒是第二个分界点,用户能感受到延迟,但是可以接受。
因此,对于一个健康的高并发系统,TP99应该控制在200毫秒以内,TP999或者TP9999应该控制在1秒以内。
❇ 可用性指标
高可用性是指系统具有较高的无故障运行能力,可用性 = 正常运行时间 / 系统总运行时间,一般使用几个9来描述系统的可用性。
对于高并发系统来说,最基本的要求是:保证3个9或者4个9。原因很简单,如果你只能做到2个9,意味着有1%的故障时间,像一些大公司每年动辄千亿以上的GMV或者收入,1%就是10亿级别的业务影响。
❇ 可扩展性指标
面对突发流量,不可能临时改造架构,最快的方式就是增加机器来线性提高系统的处理能力。
对于业务集群或者基础组件来说,扩展性 = 性能提升比例 / 机器增加比例,理想的扩展能力是:资源增加几倍,性能提升几倍。通常来说,扩展能力要维持在70%以上。
但是从高并发系统的整体架构角度来看,扩展的目标不仅仅是把服务设计成无状态就行了,因为当流量增加10倍,业务服务可以快速扩容10倍,但是数据库可能就成为了新的瓶颈。
像MySQL这种有状态的存储服务通常是扩展的技术难点,如果架构上没提前做好规划(垂直和水平拆分),就会涉及到大量数据的迁移。
因此,高扩展性需要考虑:服务集群、数据库、缓存和消息队列等中间件、负载均衡、带宽、依赖的第三方等,当并发达到某一个量级后,上述每个因素都可能成为扩展的瓶颈点。
❇ 纵向扩展(scale-up)
它的目标是提升单机的处理能力,方案又包括:
❇ 横向扩展(scale-out)
因为单机性能总会存在极限,所以最终还需要引入横向扩展,通过集群部署以进一步提高并发处理能力,又包括以下2个方向:
1、做好分层架构:这是横向扩展的提前,因为高并发系统往往业务复杂,通过分层处理可以简化复杂问题,更容易做到横向扩展。
上面这种图是互联网最常见的分层架构,当然真实的高并发系统架构会在此基础上进一步完善。比如会做动静分离并引入CDN,反向代理层可以是LVS+Nginx,Web层可以是统一的API网关,业务服务层可进一步按垂直业务做微服务化,存储层可以是各种异构数据库。
2、各层进行水平扩展:无状态水平扩容,有状态做分片路由。业务集群通常能设计成无状态的,而数据库和缓存往往是有状态的,因此需要设计分区键做好存储分片,当然也可以通过主从同步、读写分离的方案提升读性能。
❇ 高性能的实践方案
1、集群部署,通过负载均衡减轻单机压力。
上述方案无外乎从计算和 IO 两个维度考虑所有可能的优化点,需要有配套的监控系统实时了解当前的性能表现,并支撑你进行性能瓶颈分析,然后再遵循二八原则,抓主要矛盾进行优化。
❇ 高可用的实践方案
1、对等节点的故障转移,Nginx和服务治理框架均支持一个节点失败后访问另一个节点。
高可用的方案主要从冗余、取舍、系统运维3个方向考虑,同时需要有配套的值班机制和故障处理流程,当出现线上问题时,可及时跟进处理。
❇ 高扩展的实践方案
1、合理的分层架构:比如上面谈到的互联网最常见的分层架构,另外还能进一步按照数据访问层、业务逻辑层对微服务做更细粒度的分层(但是需要评估性能,会存在网络多一跳的情况)。
1、如何看待此类「面试造火箭,入职拧螺丝」的现象?
2、应届生仅仅学过 JUC 和 GC,一点宏观概念也没有,怎么回答比较好?
3、平时做的项目就几十 QPS,该如何应对?
4、很多高并发技巧都有遇到或者实操过,怎么才能形成比较系统的回答?
提前说明下:面试是一个比较复杂的场景,同时偏主观判断,下面的回答完全基于我的个人经验进行总结,所以仅做参考,也欢迎大家评论区留下你的看法。
一、如何看待此类「面试造火箭,入职拧螺丝」的现象?
知乎上有一条评论是这样的,应该能代表很多候选人的心声:
面试时,问百万高并发;入职后,写个位数 QPS 的服务。
如果是一些小公司,平时 QPS 不过百,一看就是面试官在装 X,不要被他带节奏,因为他也不懂,两个高并发小白互聊,看谁先唬住谁。
1、对于一个确实需要造火箭的岗位,或者是短期不需要造火箭但是未来有机会造火箭的岗位(比如大厂的核心业务,或者中小厂正处于快速上升期的业务),问这些问题是无可厚非的。
2、对于一个「供远大于需」的职位,即使这个岗位就是拧螺丝的,公司一定会优先选择更优秀的人,竞争越激烈,要求只会越变态,你是老板你肯定也这么做。
3、站在面试官角度,需要知道求职者的能力上限,来决定他的最终职级。而最常用的面试技巧就是:由浅到深地去问问题,直到触达求职者的盲区。所以,面试内容跟实际工作是需要分开看待的。
4、不排除有些公司,特意用造火箭的面试假象来吸引你,做的工作却很低端;或者说有些面试官就是为了秀自己的肌肉。这种情况,大家更应该关注:你未来所做的事情是否符合你的预期?你是否愿意和面试官成为同事?问清楚这些,自然就有了判断。
如果你认同上面这 4 点看法,我觉得心态上会更容易接受这种现象,既然没法改变它,那就多琢磨如何搞定它。
此外,作为一个对技术有追求的人来说,造火箭是我们的终极目标,没知识深度肯定是不行的。如果你始终只看到自己在拧螺丝,你就永远走不出这个怪圈,而为了造火箭而去拧螺丝的人,一定会在简单且机械的重复工作中,慢慢找到突破口。
所以放平心态,抓住这种面试机会,多跟有经验的人交流,也是一个学习过程。
二、应届生仅仅学过 JUC 和 GC,一点宏观概念也没有,怎么回答比较好?
先说明一点:大部分公司在面试应届生时,其实很少问高并发,至少我经历过的大小公司都这样。
为什么不问?因为高并发是一个很复杂的系统性问题,很注重业务场景以及实战经验。而应届生最缺的就是项目经验,所以很难区分出大家的水平。
但如果真问到了高并发,而自己仅学过 JUC,懂点 GC,该如何应对呢?给几点建议:
1、概念性的东西要掌握扎实,不少同学容易把「并发、并行、高并发」这几个概念弄混淆,认为高并发就是「高+并发」,建议大家多查查资料,了解清楚高并发一些常识性的知识。
2、打动面试官的一定是你的强项:高并发问题通常涉及到全链路,你能从架构层面去谈,也能程序语言的并发特性上去谈,还能从网络、操作系统层面去谈,角度非常多。
你完全可以引导面试官到你擅长的方向上:比如 JUC、高效算法或者数据结构、数据库索引、IO复用或者零拷贝等等,这些往往是应届生的强项。
三、平时做的项目就几十 QPS,该如何应对呢?
很多同学应该属于这种情况,做过不少项目,唯独缺少高并发的实战经验。我建议从下面几个方向去做针对性准备:
1、高并发有关的理论知识需要先有系统性的了解,然后再回到自己熟悉的业务上去思考:如果并发再提高 10 倍或者 100 倍,哪些环节会遇到问题,然后你会怎么去应对?有实际场景,并且你又能给出方案选型的合理依据,效果远比空谈理论要好很多。
2、对于项目中常用的各种中间件,比如缓存、消息队列、ElasticSearch 等,其实都是非常典型的高并发系统,你可以对某个中间件进行深入研究,然后面试时主动引导面试官到这种基础组件上,谈谈你的认识。
3、如果你对自己的高并发设计能力很有信心,也可以让面试官给你拟定一个业务场景,然后给出你的设计方案。
总之,为了弥补实战经验上的短板,理论知识一定扎实,不能浮于表面,生搬硬套,不然很容易留下负面印象。
四、很多高并发技巧都有遇到或者实操过,怎么才能形成比较系统的回答?
这种情况属于有高并发经验,但是平时思考偏少,又没意识去做系统性整理,面试时很容易发挥失常。
建议可以参考我的回答:分别从高性能、高可用、高扩展这 3 个大方向做一次盘点,先找出技术亮点。
每个亮点,一定要扎进细节里去做整理和深度思考,弄清楚 Why 和 How:为什么要用这个技巧?它是怎么发挥作用的?会引发哪些关联问题?最终取得了什么效果?此外,业务背景以及指标性的数据都要做到心中有数。
找到亮点后,可以尝试找一条主脉络:比如按时间顺序、或者系统演进的顺序进行串联,这样比较符合高并发设计的基本原则,会让你的方案更接地气。
好啦!大概想到这些内容,希望这 4 个问题能让你有所启发,如果有其他疑问,欢迎加我微信交流或者评论区留言讨论。
高并发确实是一个复杂且系统性的问题,由于篇幅有限,诸如分布式Trace、全链路压测、柔性事务都是要考虑的技术点。另外,如果业务场景不同,高并发的落地方案也会存在差异,但是总体的设计思路和可借鉴的方案基本类似。
高并发设计同样要秉承架构设计的3个原则:简单、合适和演进。“过早的优化是万恶之源”,不能脱离业务的实际情况,更不要过度设计,合适的方案就是最完美的。
大家在看:
武哥知乎link:https://www.zhihu.com/question/421237964/answer/1829950532
武哥漫谈IT
前亚马逊工程师,现58转转技术总监,持续分享个人的成长经历,希望为你的职场发展带来些新思路,欢迎扫码关注我!