这篇文章,说说很多同学问我的一个问题。面试的时候被问到一个很迷惑的问题:你们的系统是如何支持高并发的?一道面试题的背景介绍了大部分同学。当我问这个问题时,我没有任何想法来回答它。我不知道从哪里开始。其实本质是我没有体验过一些真正的高并发系统。因为我没有相关的项目经验,所以无法从自己的真实经历和经历中提炼出一套答案,然后系统的阐述我一直负责的系统是如何支持高并发的。因此,本文将从这个角度简单谈谈这个问题,用最简单的思路来回答,大致是怎么处理的。当然,这里先有一个前提:高并发系统不一样。比如每秒百万级并发的中间件系统,每天百亿级请求的网关系统,瞬时每秒数十万级请求的秒杀推广系统。他们在处理高并发的时候,因为系统的特点不同,应对的架构也不同。另外,比如电商平台中的订单系统、商品系统、库存系统在高并发场景下的架构设计是不同的,因为它们背后的业务场景是不同的。所以这篇文章主要是给大家提供一个回答这类问题的思路,不涉及任何复杂的架构设计,让你在面试的时候被问到这个问题的时候不会盯着面试官看。具体来说,如果你在面试的时候真的能回答这个问题,我建议你参考这篇文章的思路,然后对你负责的系统多思考,最后做一些相关的架构实践。我们先考虑最简单的系统架构。假设你的系统一开始部署在一台机器上,后面接了一个数据库,数据库部署在一台服务器上。我们甚至可以更加现实。比如你系统部署的机器是4核8G,数据库服务器是16核32G。此时,假设你的系统总共有100,000个用户,而且用户数量很少。日活用户因系统场景不同而不同。我们取一个更客观的比例,10%,日活跃用户数为10000。按照28的规律,每天高峰期4小时算,高峰期活跃用户比例达到80%,即4小时内有8000人活跃。然后每个人都向你的系统发起请求,算他一天20次吧。那么高峰期只有8000人发起的16万个请求,4小时内平均每秒10个请求(14400秒)。出色地!完全不符合高并发吧?然后系统层面每秒10个请求,每次请求数据库都会有几次数据库操作,比如做crud之类的。那我们就拿一个请求对应3个数据库请求吧。在这种情况下,数据库层每秒只有30个请求,对吗?按照这个数据库服务器的配置,支持是绝对没问题的。上述系统,用图表示如下:系统集群部署假设此时你的用户数开始快速增长,比如注册用户数增长50倍,达到500万。此时日活跃用户50万,高峰期每秒对系统的请求为500/s。那么每秒对数据库的请求数是1500/s,这时候会发生什么呢?根据上面的机器配置,如果你的系统处理一些比较复杂的业务逻辑,就是那种业务逻辑重的系统,CPU比较密集。这时候,当4核8G的机器每秒请求达到500/s的时候,很可能你会发现你机器的CPU负载很高。然后在数据库层面,基于上面的配置,其实峰值请求压力基本1500/s还是可以接受的。这个主要是观察数据库所在机器的磁盘负载、网络负载、CPU负载、内存负载。根据我们线上的经验,配置的数据库在1500/s的请求压力下是没有问题的。所以这时候你需要做的一件事就是首先支持你系统的集群部署。可以在前面挂一个负载均衡层,将请求平均发送到系统层面,这样系统就可以使用多台机器集群来支撑更高的并发压力。例如,假设系统再部署一台机器,每台机器只会有250/s的请求。这样两台机器的CPU负载都会明显降低。这个最初的“高并发”不应该先覆盖吗?如果连这个都不做,当单机负载越来越高的时候,在极端情况下下面是机器上部署的系统可能没有足够的资源来响应请求,然后请求可能会卡住,甚至系统可能会崩溃。所以,简单总结一下,第一步要做的:加一个负载均衡层,把请求均匀的发到系统层。系统层采用集群部署多台机器来承受初期的并发压力。此时的架构图变成了如下:分库分库分表+读写分离。假设此时用户数量继续增长,达到1000万注册用户,则日活跃用户为100万。那么这个时候系统级别的请求量会达到每秒1000/s。在系统层面,可以通过集群不断扩容。反正前面的负载均衡层会平均分配流量。但是此时数据库层面接受的请求数会达到3000/s,有点问题。这时候数据库层面的并发请求翻倍了,你肯定会发现线上的数据库负载越来越高。每到高峰期,磁盘IO、网络IO、内存消耗、CPU负载的压力都会很大,大家都担心数据库服务器能不能承受得住。没错,一般来说,对于普通配置的在线数据库,建议把并发读写加起来。按照我们上面举例的配置,应该不会超过3000/s。因为数据库压力太大,第一个问题就是高峰期可能会降低系统性能,因为数据库负载高会影响性能。还有一个,你的数据库压力太大挂了怎么办?所以这时候你就得把系统分库分表+读写分离,也就是把一个数据库拆分成多个库,部署在多个数据库中。在数据库服务器上,这是托管写请求的主数据库。然后每个主库挂载至少一个从库,从库承载读请求。此时假设数据库层面的读写并发为3000/s,其中写并发占1000/s,读并发占2000/s。那么数据库分表后,主库部署在两台数据库服务器上,支持写请求,每台服务器承载的写并发为500/s。每个主库挂载一台服务器部署一个从库,所以两个从库支持的读并发都是1000/s。简单总结一下,当并发量不断增长的时候,我们需要关注数据库层面:数据库和表分离,读写分离。此时的架构图如下:引入缓存集群就好办了。如果你注册的用户越来越多,这个时候可以继续加机器。例如,可以在系统级别添加机器。承载更高的并发请求。然后,如果数据库层面的写并发越来越高,扩容,增加数据库服务器。扩容机可以支持分库分表。如果数据库层面的读并发越来越高,扩容,增加从库。但是这里有个很大的问题:数据库本身并不是用来承载高并发请求的,所以一般来说,单台数据库机器每秒承载的并发量在几千量级,而数据库使用的机器都是比较高配置的,比较贵的机器,性价比非常高。如果只是简单地不断增加机器,其实是错误的。所以在高并发架构中通常会有一个缓存环节。缓存系统的设计是为了承载高并发。所以单机承载的并发量是每秒几万甚至几十万,高并发的承载能力比数据库系统高出一到两个数量级。因此,可以根据系统的业务特点,针对写少读多的请求引入缓存集群。具体来说,就是在写数据库的时候,同时向缓存集群写入一份数据,然后缓存集群来承载大部分的读请求。这样通过缓存集群,可以用更少的机器资源来承载更高的并发。比如上图中,读请求目前是2000/s/s,两个从库各抗1000/s的读请求,但是1800/s的读请求可以直接从缓存中读取。改变数据。那么这个时候一旦引入缓存集群,就可以抵抗1800/s的读请求,而数据库层面的读请求是200/s。同样,这里给大家放一张架构图,大家一起感受下:按照上面的架构,它有什么好处呢?可能以后你的系统每秒读请求数万次,但是可能80%~90%是通过缓存集群读取的,缓存集群中的机器可能支持每秒数万次的读请求秒每秒,所以对机器资源的消耗很小,可能两三台机器就够了。如果换个数据库试试看,可能要不停的加从库到10、20台机器才能抵抗每秒几万的并发读,成本极高。好吧,简单总结一下承载高并发需要考虑的第三点:不要盲目扩容数据库。数据库服务器价格昂贵,不是用来承载高并发的。针对写少读多的请求,引入缓存集群来抵抗大量的读请求。介绍消息中间件集群再看数据库写的压力,其实和读的差不多。如果说你所有的写请求都落在数据库的主库层,当然没有问题,但是写压力越来越大怎么办?比如每秒写入数万条数据。这个时候,是不是也马不停蹄?您可以将机器添加到主数据库吗?当然可以,但是同样的,你会消耗大量的机器资源,这是由数据库系统的特性决定的。同样的资源下,数据库系统过于繁重复杂,并发承载量在几千/s量级,这时候就需要引入一些其他的技术。比如消息中间件技术,也就是MQ集群,可以很好的对写请求做异步处理,达到削峰填谷的效果。比如你当前每秒写1000/s的写请求,比如500个请求必须请求立即写入数据库,但是另外500个写请求可以让异步等待几十秒,甚至几秒在进入数据库之前的几分钟。那么这个时候就可以完全引入一个消息中间件集群,每秒500个允许异步的请求写入MQ,然后基于MQ做削峰填谷。例如,它可以以100/s的稳定速率被消耗,然后落入数据库。这时候数据库的写入压力会大大降低。这时候架构图就变成了如下:看上面的架构图,首先消息中间件系统本身也是为高并发而生的,所以通常单机支持几万甚至几十万并发要求。所以,它本身就和缓存系统一样,可以用很少的资源支持非常高的并发请求。用它来支持一些允许异步的高并发写是没有问题的,比直接用数据库来支持那些高并发请求要好。要求大量减少机器使用。而且经过消息中间件削峰填谷之后,比如你写数据库的速度稳定在100/s,那么数据库层面收到的写请求的压力就会变成500/s+100/s=600/s什么?看看,有没有发现数据库的压力变小了?至此,通过以下手段,我们已经能够让系统架构以尽可能小的机器资源来抵抗***的请求压力,减轻数据库的压力负担:系统集群。分库分表+数据库级别的读写分离。针对读多写少的请求,引入缓存集群。针对编写压力大,引入了消息中间件集群。至此,一个简单的高并发系统的描述就结束了。然而,故事远没有结束。现在的高并发面试题你能hold住吗?看完这篇文章,你觉得你能答对面试中的高并发题吗?不幸的是,答案是否定的。而且我认为你是绝对不可能仅仅靠几篇文章就完全回答这个问题的。这件事情是由很多原因导致的。首先,高并发这个话题本身就很复杂,远不是一些文章能说清楚的。其本质在于,真正支持复杂业务场景的高并发系统架构其实是非常复杂的。例如每秒百万级并发的中间件系统,每天百亿级请求的网关系统,瞬时每秒数十万级请求的闪电促销系统,大型高并发电商平台支持数亿用户的架构等。为了支持高并发请求,在设计系统架构时,会结合具体的业务场景和特点设计各种复杂的架构,这需要大量的底层技术支持,以及成熟的架构和机制设计能力。最终,各种复杂系统所呈现出的架构复杂性,将远远超出大多数没有接触过的同学的想象。但如此复杂的系统架构,很难通过一些文章来解释其中的各种细节和落地生产的过程。其次,高并发这个话题本身包含的远不止本文提到的话题:分库分表、缓存、消息。一个完整复杂的高并发系统架构必然包括:各种复杂的自研基础设施系统。各种精巧的架构设计(如热点缓存架构设计、多优先级高吞吐MQ架构设计、系统全链路并发性能优化设计等)。还有各种复杂系统组成的高并发架构整体技术方案。还有NoSQL(Elasticsearch等)/负载均衡/Web服务器等相关技术。所以大家一定要记住对技术要有敬畏之心,这些东西是很难通过一些文章表达清楚的。***,真正实现生产的时候,你的系统在高并发场景下会出现很多技术问题。比如消息中间件的吞吐量需要优化,磁盘写压力太大,性能太差,内存消耗太大,容易爆,分库分表中间件不知道为什么数据会丢失,等等。诸如此类的问题太多了,不可能通过文章都说清楚。这篇文章能给你带来什么启示?其实这篇文章的定位是对高并发这个面试题目做一个扫盲,因为我发现大部分来问我这个问题的同学,连最基本的高并发架构演化思路都在这讲文章可能看不懂。当然,也是因为毕竟没有真正做过高并发系统,没有相关经验。这个问题真的很难理解。所以这篇文章就是让很多没接触过的同学有个初步的认知,这个高并发是怎么回事,系统的压力在哪里,系统架构需要引入什么来更好的支持它。高并发压力。而你也可以沿着本文的思路继续思考,结合一些你熟悉和知道的技术。比如你熟悉Elasticsearch技术,那么你可以想一下,在高并发架构下,是否可以通过分布式架构的ES技术来支持高并发搜索?大家一定要多思考,多画图,盘点一下手头系统的请求压力。计算分配给各个中间件层级的请求压力,如何用最少的机器资源来支持更高并发的请求。这是一个很好的高并发架构设计思路。如果达到这个效果,这篇文章就成功了。其他的,我还是建议同学们多想想高并发这个话题,结合自己负责的系统。比如现在的业务场景,你的系统有多大的请求压力?如果请求压力增加10倍,你的架构如何支撑?如果请求压力增加100倍,你的架构如何支撑?如果请求压力增加1000倍,你的架构如何支撑?如何支撑架构?平时一定要给自己设置一些技术上的挑战,督促自己去思考自己的系统,最好在架构上多做演练、实现和实践,实际操作起来会有更好的感悟。然后在面试的过程中,至少我做了一定的深度思考,结合自己负责的系统做了一些实践,这样才能和面试官有一个更清晰系统的交代。虽然大部分同学可能没有机会去体验真正的大型超高并发系统架构的设计,但是如果这篇文章能够让大家对自己的项目多一些思考的话。在面试的过程中,如果有一些系统的想法和阐述,那么本文的目的就达到了。作者:中华狮山中华狮山:十余年BAT架构经验,一线互联网公司技术总监。带领数百人团队开发过亿级大流量高并发系统。多年工作积累的研究手稿和经验总结,现整理成文,一一传授。微信公众号:石山的建筑笔记(ID:shishan100)。
