高并发是几乎每个程序员都想拥有的体验。原因很简单:随着流量的增加,会遇到各种技术问题,比如接口响应超时、CPU负载增加、GC频繁、死锁、数据存储量大等,这些问题可以驱使我们在技术深度上不断提升。在以往的面试中,如果应聘者做过高并发项目,我一般会请对方谈谈对高并发的理解,但是能够系统回答这个问题的人并不多。大致可以分为以下几类:1.对数据指标没有概念:不知道选择什么指标来衡量高并发系统?分不清并发和QPS,连自己系统的总用户数、活跃用户数、平均峰值时间都不知道。QPS、TPS等关键数据。2.设计了一些方案,但没有把握好细节:无法说出方案的技术要点和可能的副作用。比如读性能有瓶颈,就会引入缓存,而忽略缓存命中率、热键、数据一致性等问题。3、片面理解,将高并发设计等同于性能优化:大谈并发编程、多级缓存、异步、横向扩展,而忽略了高可用设计、服务治理、运维保障。4.掌握了大计划,却忽略了最基本的东西:垂直分层、水平分区、缓存等大思路我能说清楚,但不自觉分析数据结构是否合理,算法是否合理高效的。从来没有想过从最基础的IO入手,并计算两个维度去优化细节。在这篇文章中,我想结合自己在高并发项目中的经历,系统地总结一下高并发需要掌握的知识和实践思路。希望对您有所帮助。内容分为以下三个部分:如何理解高并发?高并发系统设计的目标是什么?高并发有哪些实用的解决方案?01如何理解高并发?高并发意味着大流量,需要通过技术手段来抵御流量的冲击,这些手段就像是在操纵流量,让流量更顺畅的被系统处理,给用户带来更好的体验。我们常见的高并发场景有:淘宝的双11、春运抢票、微博大V的热点新闻等等。除了这些典型的东西,每秒几十万请求的秒杀系统,每天几千万订单的订单系统,每天几亿日活动量的信息流系统,都可以归为高并发。显然,上面提到的高并发场景,并发量是不同的,那么并发到多少才算是高并发呢?1、不能只看数字,要看具体的业务场景。不能说10WQPS的秒杀是高并发,1WQPS的信息流不是高并发。信息流场景涉及复杂的推荐模型和各种人工策略,其业务逻辑可能比秒杀场景复杂10倍以上。所以,不在同一个次元,就没有比较的意义。2、业务是从0到1做的,并发和QPS只是参考指标。最重要的是:在业务量逐渐变成原来10倍、100倍的过程中,你有没有使用高并发?处理方式是演化你的系统,从架构设计、编码实现,甚至产品方案的维度来预防和解决高并发带来的问题?而不是盲目升级硬件、加机器横向扩展。另外,每个高并发场景的业务特点也完全不同:有读多写少的信息流场景,也有读多写多的事务场景。有没有一个通用的技术方案来解决不同场景的高并发问题?我觉得是一个大思路,可以借鉴,也可以参考别人的方案,但是在实际执行的过程中,在细节上会有无数的坑。另外,由于软硬件环境、技术栈、产品逻辑不能完全一致,所有这些都会导致相同的业务场景。即使采用相同的技术方案,也会面临不同的问题,这些坑都得一一探访。因此,在这篇文章中,我将重点介绍我实践过的基础知识、大致思路和有效经验,希望能让大家对高并发有更深入的了解。02高并发系统设计的目标是什么?先搞清楚高并发系统设计的目标,再在此基础上讨论设计方案和实践经验才有意义、中肯。2.1宏观目标高并发并不意味着只追求高性能。这是很多人片面的认识。从宏观上看,高并发系统设计的三个目标:高性能、高可用性、高扩展性。1、高性能:性能体现了系统的并行处理能力。在硬件投资有限的情况下,提高性能就意味着节约成本。同时,性能也体现了用户体验。响应时间分别为100毫秒和1秒,给用户带来完全不同的感受。2.高可用性:表示系统可以正常服务的时间。一是全年无休,无故障;另一个时不时出现上网事故和宕机,用户必须选择前者。另外,如果系统只能做到90%可用,也会极大地拖累业务。3.高扩展:表示系统的扩展能力,是否能在流量高峰时短时间内完成扩展,更流畅的处理高峰流量,比如双十一活动,明星离婚等热点事件。这三个目标需要统筹考虑,因为它们相互关联,甚至相互影响。例如:考虑到系统的可扩展性,你会把服务设计成无状态的。这种集群设计保证了高扩展性,实际上间接提高了系统的性能和可用性。又如:为了保证可用性,通常会在服务接口上设置超时设置,防止大量线程阻塞在慢请求上,造成系统雪崩。超时设置的合理性如何?一般我们会参考依赖服务的性能。设置。2.2微观目标从微观的角度来看,衡量高性能、高可用、高扩展的具体指标有哪些?为什么选择这些指标?性能指标可以通过性能指标衡量当前的性能问题,同时作为性能优化的评价依据。一般来说,以一段时间内的界面响应时间作为指标。1、平均响应时间:最常用,但缺陷很明显,对慢请求不敏感。比如10000个请求,其中9900个是1ms,100个是100ms,平均响应时间是1.99ms。虽然平均耗时只增加了0.99ms,但是1%的请求响应时间却增加了100倍。2、TP90、TP99等分位数值:将响应时间从小到大排序,TP90代表第90个百分位的响应时间。分位数越大,对慢速请求越敏感。3.吞吐量:与响应时间成反比。例如,如果响应时间为1ms,则吞吐量为每秒1000次。通常,在设置性能目标时,吞吐量和响应时间都会被考虑在内。比如每秒10000个请求,AVG控制在50ms以下,TP99控制在100ms以下。对于高并发系统,必须同时考虑AVG和TP分位数。另外,从用户体验的角度来看,200毫秒被认为是第一个分界点,用户不会感觉到延迟,而1秒是第二个分界点,用户会感觉到延迟,但是可接受的。所以一个健康的高并发系统,TP99应该控制在200毫秒以内,TP999或者TP9999应该控制在1秒以内。可用性指标高可用性意味着系统具有很高的无故障运行能力。可用性=平均故障时间/系统总运行时间。一般用几个9来描述系统的可用性。对于高并发系统,最基本的要求是:保证3个9或者4个9。原因很简单。如果你只能做到两个9,那就意味着还有1%的停机时间。像一些每年GMV或者营收上千亿的大公司,1%就是10亿的业务影响。ScalabilityIndex面对突发流量,临时修改架构是不可能的。最快的方法是通过增加机器来线性增加系统的处理能力。对于业务集群或者基础组件,可扩展性=性能提升率/机器增加率,理想的可扩展性是:资源增加几倍,性能增加几倍。一般来说,扩容应该保持在70%以上。但从高并发系统的整体架构来看,扩容的目标不仅仅是将服务设计成无状态,因为当流量增加10倍时,业务服务可以快速扩容10倍,但是数据库可能会变成一个新的。瓶颈。像MySQL这样的有状态存储服务,在扩展上通常是一个技术难点。如果没有提前规划好架构(纵横拆分),就会涉及到大量数据的迁移。因此,高可扩展性需要考虑:服务集群、数据库、缓存和消息队列等中间件、负载均衡、带宽、依赖的第三方等。当并发量达到一定水平时,以上每一个因素都可能变得可扩展。瓶颈点。03高并发实用方案有哪些?了解了高并发设计的三大目标后,我们再系统总结一下高并发设计的解决方案,将从以下两部分入手:先总结通用的设计方法,再着重介绍高并发设计的具体实践给出了性能、高可用性和高可扩展性的解决方案。3.1通用设计方法通用设计方法主要从“纵向”和“横向”两个维度入手,俗称高并发处理的两个轴:垂直扩展和水平扩展。纵向扩展(scale-up)的目标是提高单机的处理能力。方案包括:1.提升单机硬件性能:通过增加内存、CPU核心数、存储容量,或将磁盘升级为SSD等堆硬件方式进行提升。2、提高单机软件性能:使用缓存减少IO次数,使用并发或异步方式提高吞吐量。水平扩展(scale-out)因为单机的性能永远是有限的,所以最终需要通过集群部署引入水平扩展,进一步提升并发处理能力,包括以下两个方向:1.做一个好的jobinlayeredarchitecture:这是Advance横向扩展,因为高并发系统往往业务复杂,复杂的问题可以通过分层处理来简化,更容易实现横向扩展。上图是互联网最常见的分层架构。当然,真正的高并发系统架构还要在此基础上进一步完善。比如动静分离,引入CDN。反向代理层可以是LVS+Nginx,web层可以是统一的API网关,业务服务层可以根据垂直业务进一步微服务,存储层可以是各种异构数据库。.2、各层横向扩展:无状态横向扩展,有状态分片路由。业务集群通常可以设计成无状态的,而数据库和缓存通常是有状态的。因此,需要为存储分片设计分区键。当然也可以通过主从同步和读写分离来提高读性能。3.2具体的实用方案下面结合我个人的经验,总结一下在高性能、高可用、高扩展性方面可以实现的实用方案。高性能实用方案1.集群部署,通过负载均衡降低单机压力。2、多级缓存,包括使用CDN的静态数据、本地缓存、分布式缓存等,以及缓存场景下热键的处理、缓存穿透、缓存并发、数据一致性等。3、分库分??表和索引优化,借助搜索引擎解决复杂的查询问题。4、考虑使用NoSQL数据库,如HBase、TiDB等,但团队必须熟悉这些组件,并有较强的运维能力。5.异步,通过多线程、MQ,甚至是延迟任务,对二级进程进行异步处理。6、限流需要考虑业务是否允许限流(比如允许秒杀场景),包括前端限流、Nginx接入层限流、服务端限流。7、流量削峰填谷,通过MQ承接流量。8.并发处理,通过多线程将串行逻辑并行化。9、预计算,比如抢红包的场景,可以提前计算好红包的数量并缓存起来,发红包的时候直接使用。10、缓存预热,通过异步任务将数据提前预热到本地缓存或分布式缓存。11、减少IO次数,比如数据库和缓存的批量读写,RPC批量接口支持,或者通过冗余数据killRPC调用。12、减少IO时数据包的大小,包括采用轻量级的通信协议,合适的数据结构,去除接口中的冗余字段,减少缓存键的大小,压缩缓存值。13、程序逻辑优化,如前置高概率阻塞执行过程的判断逻辑,优化For循环的计算逻辑,或采用更高效的算法。14.各种池化技术的使用和池大小设置,包括HTTP请求池、线程池(设置核心参数考虑CPU密集型还是IO密集型)、数据库和Redis连接池等15.JVM优化,包括大小新生代和老年代,GC算法的选择等,尽可能降低GC频率和时间消耗。16、锁的选择,对于读多写少的场景使用乐观锁,或者考虑使用分段锁来减少锁冲突。上述方案无非是从计算和IO两个维度考虑了所有可能的优化点。需要一个配套的监控系统,实时了解当前性能,支持你进行性能瓶颈分析,然后按照第28条原理,抓住主要矛盾进行优化。高可用实践方案1.对等节点故障转移。Nginx和服务治理框架都支持一个节点失效后访问另一个节点。2.非对等节点的故障转移,通过心跳检测,实现主备切换(如redis哨兵模式或集群模式,MySQL主从切换等)。3.接口层面的超时设置、重试策略和幂等设计。4、降级处理:保证核心服务,牺牲非核心服务,必要时熔断;或者当核心链接出现问题时,还有一个替代链接。5、限流处理:对超过系统处理能力的请求直接拒绝或返回错误码。6、MQ场景下的消息可靠性保证,包括producer端的retry机制,broker端的持久化,consumer端的ack机制。7、灰度发布,可支持小流量按机器维度部署,观察系统日志和业务指标,运行稳定后全量推送。8、监控告警:完善的监控系统,包括对CPU、内存、磁盘、网络最基本的监控,以及对Web服务器、JVM、数据库、各种中间件、业务指标的监控。9、容灾演练:类似于现在的“混沌工程”,对系统进行一些破坏性的手段,观察局部故障是否会导致可用性问题。高可用方案主要从冗余、权衡、系统运维三个方向考虑。同时需要有配套的职责机制和故障处理流程。在线出现问题时,可以及时跟进。高扩展实践方案1.合理的分层架构:比如上面提到的互联网最常见的分层架构,根据数据访问层和业务逻辑层对微服务进一步细粒度分层(但性能需要评估,并且网络中会有一个额外的跃点)。2、存储层的拆分:按业务维度进行纵向拆分,再按数据特征维度(分库分表)进一步横向拆分。3、业务层的拆分:最常见的拆分是基于业务维度(如电商场景下的商品服务、订单服务),或者按照核心接口和非核心接口拆分,或者按照请求来源拆分(比如ToC和ToB,APP和H5)。最后,高并发确实是一个复杂的系统性问题。限于篇幅,分布式溯源、全链路压测、灵活交易等技术点均需考虑。另外,如果业务场景不同,高并发的实现方案也会有所不同,但总体设计思路基本类似,可以参考的方案。高并发设计也必须坚持架构设计的三个原则:简洁、适度、进化。“过早的优化是万恶之源。”不能脱离业务实际,更不能过度设计。合适的解决方案才是最完美的。作者:罗君武,现任58转转技术总监,前亚马逊工程师,分享技术难点、复杂系统架构、管理方法和职场认知。
