“高并发,几乎是每个程序员都想拥有的经验。原因很简单:随着流量变大,会遇到各种各样的技术问题。

图片来自 Pexels

比如接口响应超时、CPU load 升高、GC 频繁、死锁、大数据量存储等等,这些问题能推动我们在技术深度上不断精进。

在过往的面试中,如果候选人做过高并发的项目,我通常会让对方谈谈对于高并发的理解。

但是能系统性地回答好此问题的人并不多,大概分成这样几类:

  • 对数据化的指标没有概念:不清楚选择什么样的指标来衡量高并发系统?分不清并发量和 QPS,甚至不知道自己系统的总用户量、活跃用户量,平峰和高峰时的 QPS 和 TPS 等关键数据。

  • 设计了一些方案,但是细节掌握不透彻:讲不出该方案要关注的技术点和可能带来的副作用。比如读性能有瓶颈会引入缓存,但是忽视了缓存命中率、热点 Key、数据一致性等问题。

  • 理解片面,把高并发设计等同于性能优化:大谈并发编程、多级缓存、异步化、水平扩容,却忽视高可用设计、服务治理和运维保障。

  • 掌握大方案,却忽视最基本的东西:能讲清楚垂直分层、水平分区、缓存等大思路,却没意识去分析数据结构是否合理,算法是否高效,没想过从最根本的 IO 和计算两个维度去做细节优化。

这篇文章,我想结合自己的高并发项目经验,系统性地总结下高并发需要掌握的知识和实践思路,希望对你有所帮助。

内容分成以下三个部分:

  • 如何理解高并发?

  • 高并发系统设计的目标是什么?

  • 高并发的实践方案有哪些?

如何理解高并发?

高并发意味着大流量,需要运用技术手段抵抗流量的冲击,这些手段好比操作流量,能让流量更平稳地被系统所处理,带给用户更好的体验。

我们常见的高并发场景有:淘宝的双 11、春运时的抢票、微博大 V 的热点新闻等。

除了这些典型事情,每秒几十万请求的秒杀系统、每天千万级的订单系统、每天亿级日活的信息流系统等,都可以归为高并发。

很显然,上面谈到的高并发场景,并发量各不相同,那到底多大并发才算高并发呢?

不能只看数字,要看具体的业务场景。不能说 10W QPS 的秒杀是高并发,而 1W QPS 的信息流就不是高并发。

信息流场景涉及复杂的推荐模型和各种人工策略,它的业务逻辑可能比秒杀场景复杂 10 倍不止。因此,不在同一个维度,没有任何比较意义。

业务都是从 0 到 1 做起来的,并发量和 QPS 只是参考指标,最重要的是:在业务量逐渐变成原来的 10 倍、100 倍的过程中,你是否用到了高并发的处理方法去演进你的系统。

从架构设计、编码实现、甚至产品方案等维度去预防和解决高并发引起的问题?而不是一味的升级硬件、加机器做水平扩展。

此外,各个高并发场景的业务特点完全不同:有读多写少的信息流场景、有读多写多的交易场景,那是否有通用的技术方案解决不同场景的高并发问题呢?

我觉得大的思路可以借鉴,别人的方案也可以参考,但是真正落地过程中,细节上还会有无数的坑。

另外,由于软硬件环境、技术栈、以及产品逻辑都没法做到完全一致,这些都会导致同样的业务场景,就算用相同的技术方案也会面临不同的问题,这些坑还得一个个趟。

因此,这篇文章我会将重点放在基础知识、通用思路、和我曾经实践过的有效经验上,希望让你对高并发有更深的理解。

高并发系统设计的目标是什么?

先搞清楚高并发系统设计的目标,在此基础上再讨论设计方案和实践经验才有意义和针对性。

宏观目标

高并发绝不意味着只追求高性能,这是很多人片面的理解。从宏观角度看,高并发系统设计的目标有三个:高性能、高可用,以及高可扩展。

①高性能:性能体现了系统的并行处理能力,在有限的硬件投入下,提高性能意味着节省成本。

同时,性能也反映了用户体验,响应时间分别是 100 毫秒和 1 秒,给用户的感受是完全不同的。

②高可用:表示系统可以正常服务的时间。一个全年不停机、无故障;另一个隔三差五出线上事故、宕机,用户肯定选择前者。另外,如果系统只能做到 90% 可用,也会大大拖累业务。

③高扩展:表示系统的扩展能力,流量高峰时能否在短时间内完成扩容,更平稳地承接峰值流量,比如双 11 活动、明星离婚等热点事件。

这 3 个目标是需要通盘考虑的,因为它们互相关联、甚至也会相互影响。

比如说:考虑系统的扩展能力,你会将服务设计成无状态的,这种集群设计保证了高扩展性,其实也间接提升了系统的性能和可用性。

再比如说:为了保证可用性,通常会对服务接口进行超时设置,以防大量线程阻塞在慢请求上造成系统雪崩,那超时时间设置成多少合理呢?一般,我们会参考依赖服务的性能表现进行设置。

微观目标

再从微观角度来看,高性能、高可用和高扩展又有哪些具体的指标来衡量?为什么会选择这些指标呢?

性能指标:通过性能指标可以度量目前存在的性能问题,同时作为性能优化的评估依据。一般来说,会采用一段时间内的接口响应时间作为指标。

①平均响应时间:最常用,但是缺陷很明显,对于慢请求不敏感。比如 1 万次请求,其中 9900 次是 1ms,100 次是 100ms,则平均响应时间为 1.99ms,虽然平均耗时仅增加了 0.99ms,但是 1% 请求的响应时间已经增加了 100 倍。

②TP90、TP99 等分位值:将响应时间按照从小到大排序,TP90 表示排在第 90 分位的响应时间, 分位值越大,对慢请求越敏感。

③吞吐量:和响应时间呈反比,比如响应时间是 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 这种有状态的存储服务通常是扩展的技术难点,如果架构上没提前做好规划(垂直和水平拆分),就会涉及到大量数据的迁移。

因此,高扩展性需要考虑:服务集群、数据库、缓存和消息队列等中间件、负载均衡、带宽、依赖的第三方等,当并发达到某一个量级后,上述每个因素都可能成为扩展的瓶颈点。

高并发的实践方案有哪些?

了解了高并发设计的 3 大目标后,再系统性总结下高并发的设计方案,会从以下两部分展开:先总结下通用的设计方法,然后再围绕高性能、高可用、高扩展分别给出具体的实践方案。

通用的设计方法

通用的设计方法主要是从「纵向」和「横向」两个维度出发,俗称高并发处理的两板斧:纵向扩展和横向扩展。

纵向扩展(scale-up):它的目标是提升单机的处理能力。

方案包括如下两种:

  • 提升单机的硬件性能:通过增加内存、CPU 核数、存储容量、或者将磁盘升级成 SSD 等堆硬件的方式来提升。

  • 提升单机的软件性能:使用缓存减少 IO 次数,使用并发或者异步的方式增加吞吐量。

横向扩展(scale-out):因为单机性能总会存在极限,所以最终还需要引入横向扩展,通过集群部署以进一步提高并发处理能力。

包括以下两个方向:

①做好分层架构:这是横向扩展的提前,因为高并发系统往往业务复杂,通过分层处理可以简化复杂问题,更容易做到横向扩展。

上面这种图是互联网最常见的分层架构,当然真实的高并发系统架构会在此基础上进一步完善。

比如会做动静分离并引入 CDN,反向代理层可以是 LVS+Nginx,Web 层可以是统一的 API 网关,业务服务层可进一步按垂直业务做微服务化,存储层可以是各种异构数据库。

②各层进行水平扩展:无状态水平扩容,有状态做分片路由。业务集群通常能设计成无状态的,而数据库和缓存往往是有状态的,因此需要设计分区键做好存储分片,当然也可以通过主从同步、读写分离的方案提升读性能。

具体的实践方案

下面再结合我的个人经验,针对高性能、高可用、高扩展 3 个方面,总结下可落地的实践方案。

高性能的实践方案:

  • 集群部署,通过负载均衡减轻单机压力。

  • 多级缓存,包括静态数据使用 CDN、本地缓存、分布式缓存等,以及对缓存场景中的热点 Key、缓存穿透、缓存并发、数据一致性等问题的处理。

  • 分库分表和索引优化,以及借助搜索引擎解决复杂查询问题。

  • 考虑 NoSQL 数据库的使用,比如 HBase、TiDB 等,但是团队必须熟悉这些组件,且有较强的运维能力。

  • 异步化,将次要流程通过多线程、MQ、甚至延时任务进行异步处理。

  • 限流,需要先考虑业务是否允许限流(比如秒杀场景是允许的),包括前端限流、Nginx 接入层的限流、服务端的限流。

  • 对流量进行削峰填谷,通过 MQ 承接流量。

  • 并发处理,通过多线程将串行逻辑并行化。

  • 预计算,比如抢红包场景,可以提前计算好红包金额缓存起来,发红包时直接使用即可。

  • 缓存预热,通过异步任务提前预热数据到本地缓存或者分布式缓存中。

  • 减少 IO 次数,比如数据库和缓存的批量读写、RPC 的批量接口支持、或者通过冗余数据的方式干掉 RPC 调用。

  • 减少 IO 时的数据包大小,包括采用轻量级的通信协议、合适的数据结构、去掉接口中的多余字段、减少缓存 Key 的大小、压缩缓存 Value 等。

  • 程序逻辑优化,比如将大概率阻断执行流程的判断逻辑前置、For 循环的计算逻辑优化,或者采用更高效的算法。

  • 各种池化技术的使用和池大小的设置,包括 HTTP 请求池、线程池(考虑 CPU 密集型还是 IO 密集型设置核心参数)、数据库和 Redis 连接池等。

  • JVM 优化,包括新生代和老年代的大小、GC 算法的选择等,尽可能减少 GC 频率和耗时。

  • 锁选择,读多写少的场景用乐观锁,或者考虑通过分段锁的方式减少锁冲突。

上述方案无外乎从计算和 IO 两个维度考虑所有可能的优化点,需要有配套的监控系统实时了解当前的性能表现,并支撑你进行性能瓶颈分析,然后再遵循二八原则,抓主要矛盾进行优化。

高可用的实践方案:

  • 对等节点的故障转移,Nginx 和服务治理框架均支持一个节点失败后访问另一个节点。

  • 非对等节点的故障转移,通过心跳检测并实施主备切换(比如redis的哨兵模式或者集群模式、MySQL 的主从切换等)。

  • 接口层面的超时设置、重试策略和幂等设计。

  • 降级处理:保证核心服务,牺牲非核心服务,必要时进行熔断;或者核心链路出问题时,有备选链路。

  • 限流处理:对超过系统处理能力的请求直接拒绝或者返回错误码。

  • MQ 场景的消息可靠性保证,包括 Producer 端的重试机制、Broker 侧的持久化、Consumer 端的 Ack 机制等。

  • 灰度发布,能支持按机器维度进行小流量部署,观察系统日志和业务指标,等运行平稳后再推全量。

  • 监控报警:全方位的监控体系,包括最基础的 CPU、内存、磁盘、网络的监控,以及 Web 服务器、JVM、数据库、各类中间件的监控和业务指标的监控。

  • 灾备演练:类似当前的“混沌工程”,对系统进行一些破坏性手段,观察局部故障是否会引起可用性问题。

高可用的方案主要从冗余、取舍、系统运维 3 个方向考虑,同时需要有配套的值班机制和故障处理流程,当出现线上问题时,可及时跟进处理。

高扩展的实践方案:

  • 合理的分层架构:比如上面谈到的互联网最常见的分层架构,另外还能进一步按照数据访问层、业务逻辑层对微服务做更细粒度的分层(但是需要评估性能,会存在网络多一跳的情况)。

  • 存储层的拆分:按照业务维度做垂直拆分、按照数据特征维度进一步做水平拆分(分库分表)。

  • 业务层的拆分:最常见的是按照业务维度拆(比如电商场景的商品服务、订单服务等),也可以按照核心接口和非核心接口拆,还可以按照请求源拆(比如 To C 和 To B,APP 和 H5)。

最后的话

高并发确实是一个复杂且系统性的问题,由于篇幅有限,诸如分布式 Trace、全链路压测、柔性事务都是要考虑的技术点。

另外,如果业务场景不同,高并发的落地方案也会存在差异,但是总体的设计思路和可借鉴的方案基本类似。

高并发设计同样要秉承架构设计的 3 个原则:简单、合适和演进。“过早的优化是万恶之源”,不能脱离业务的实际情况,更不要过度设计,合适的方案就是最完美的。

希望这篇文章能带给你关于高并发更全面的认识,如果你也有可借鉴的经验和深入的思考,欢迎评论区留言讨论。

作者:骆俊武

编辑:陶家龙

出处:转载自微信公众号 IT 人的职场进阶(ID:BestITer)

不懂高并发,薪资凉半截!相关推荐

  1. 图解 | 看完这篇还不懂高并发中的线程与线程池,你来打我!

    来源 | 码农的荒岛求生 头图 | 视觉中国 一切要从CPU说起 你可能会有疑问,讲多线程为什么要从CPU说起呢?原因很简单,在这里没有那些时髦的概念,你可以更加清晰的看清问题的本质. CPU并不知道 ...

  2. 看完这篇还不懂高并发中的线程与线程池你来打我

    int len; void* start_point; - }; 接下来就是起名字时刻. 这个数据结构总要有个名字吧,这个结构体用来记录什么信息呢?记录的是程序在被 <一线大厂Java面试题解析 ...

  3. 面试高并发,凉了(全程高能,赶快收藏)

    大家好,我是冰河~~ 程序员小菜是一家互联网公司的开发人员,主要负责后端Java技术开发,平时的工作中以CRUD为主.从刚毕业来到公司,一转眼3年过去了,小菜突然觉得在这家公司工作没啥意思了,整天做C ...

  4. java多线程实例_多线程&高并发(全网最新:面试题+导图+笔记)面试手稳心不慌...

    前言 当你开始开始去跳槽面试的时候,明明只是一份15K的工作,却问你会不会多线程,懂不懂高并发,火箭造得让你猝及不防,结果就是凉凉:现如今市场,多线程.高并发编程.分布式.负载均衡.集群等可以说是现在 ...

  5. 跳槽入职字节跳动,给到20K16薪,只因比别人更懂多线程与高并发

    前言 当你开始开始去跳槽面试的时候,明明只是一份15K的工作,却问你会不会多线程,懂不懂高并发,火箭造得让你猝及不防,结果就是凉凉:现如今市场,多线程.高并发编程.分布式.负载均衡.集群等可以说是现在 ...

  6. 多线程&高并发(全网最新:面试题 + 导图 + 核心学习笔记)面试手稳心不慌,轻松拿下 offer,秋招跳槽必不可少的底层能力

    前言 当你开始开始去跳槽面试的时候,明明只是一份 15K 的工作,却问你会不会多线程,懂不懂高并发,火箭造得让你猝及不防,结果就是凉凉:现如今市场,多线程.高并发编程.分布式.负载均衡.集群等可以说是 ...

  7. 不懂这些高并发分布式架构、分布式系统的数据一致性解决方案,你如何能找到高新互联网工作呢?强势解析eBay BASE模式、去哪儿及蘑菇街分布式架构...

    互联网行业是大势所趋,从招聘工资水平即可看出,那么如何提升自我技能,满足互联网行业技能要求?需要以目标为导向,进行技能提升,本文主要针对高并发分布式系统设计.架构(数据一致性)做了分析,祝各位早日走上 ...

  8. 多线程和高并发搞不懂?还谈什么面试?(附面试题解析)

    前言: 作为一个 Java开发人员,多线程是一个逃不掉的话题,不管是工作还是面试,但理解起来比较模糊难懂,因为多线程程序在跑起来的时候比较难于观察和跟踪.搞懂多线程并发知识,可以在面试的时候和周围人拉 ...

  9. 高并发、多线程、分布式都不懂,你拿什么跳槽阿里、腾讯、京东?

    当提起这三个词的时候,是不是很多人都认为分布式=高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼? 确实,在一开始接触的时候,不 ...

最新文章

  1. php传递JSON数据
  2. 5G风起,四大IoT迎风爆发!智能家居与电表高速增长
  3. Java Hashtable get()方法与示例
  4. Eclipse 构建Maven项目--普通web项目 复制另外一个项目的配置文件导致的问题
  5. IIS7的CMD指令
  6. PaddlePaddle(7)—— 项目全流程实战:公共场所吸烟检测与EasyEdge部署
  7. Keras实现text classification文本二分类
  8. Android官方文档中文版
  9. vscode c语言插件_推荐学习C语言或CPP使用的代码编辑器
  10. steam 集换式卡牌挂卡软件 ASF Linux服务器搭建
  11. TF-IDF算法简介
  12. 2018年6月13日任务
  13. 软件测试的度量方法包括,软件测试过程的度量
  14. 用AndServer做安卓手机服务器
  15. Java笔记——反射常用方法
  16. ABP VNext学习日记14
  17. linux如何扫描网络漏洞,在Linux系统上用nmap扫描SSL漏洞的方法
  18. 2-2 图像算数操作—OpenCV-Python教程翻译
  19. 如果有天我们变陌生了,那么我就重新认识你(晚安心语)
  20. 解决自动分析仪条码二维码读取的方案

热门文章

  1. SSD + HDD 双硬盘卡顿问题
  2. HCL打开显示当前系统用户怎么解决_苹果手机蓝牙怎么连不上的解决步骤
  3. javaweb重定向和转发的区别
  4. 小土堆pytorch教程学习笔记P22
  5. PHP缓存工具(开源第一个项目)
  6. Jquery考试面试题(二)
  7. vue-3D-model图画不显示
  8. 软件测试工作流程总结
  9. PLSQL 数值函数
  10. 简要分析计算机的工作过程,计算机的工作原理.doc