高并发,你真的了解吗?

摘要:本文介绍高并发系统的度量指标,讲述高并发系统的设计思路,再梳理高并发的关键技术,最后结合做者的经验作一些延伸探讨。

当前,数字化在给企业带来业务创新,推进企业高速发展的同时,也给企业的IT软件系统带来了严峻的挑战。面对流量高峰,不一样的企业是如何经过技术手段解决高并发难题的呢?程序员

0、引言

软件系统有三个追求:高性能、高并发、高可用,俗称三高。三者既有区别也有联系,门门道道不少,全面讨论须要三天三夜,本篇讨论高并发。面试

高并发(High Concurrency)。并发是操做系统领域的一个概念,指的是一段时间内多任务流交替执行的现象,后来这个概念被泛化,高并发用来指大流量、高请求的业务情景,好比春运抢票,电商双十一,秒杀大促等场景。算法

不少程序员天天忙着搬砖,平时接触不到高并发,哪天受不了跑去面试,还经常会被面试官犀利的高并发问题直接KO,其实吧,高并发系统也不高深,我保证任何一个智商在线的看过这篇文章后,都能打败恐惧,重拾生活的信心。数据库

本文先介绍高并发系统的度量指标,而后讲述高并发系统的设计思路,再梳理高并发的关键技术,最后结合做者的经验作一些延伸探讨。编程

一、高并发的度量指标

既然是高并发系统,那并发必定要高,否则就名存实亡。并发的指标通常有QPS、TPS、IOPS,这几个指标都是可归为系统吞吐率,QPS越高系统能hold住的请求数越多,但光关注这几个指标不够,咱们还须要关注RT,即响应时间,也就是从发出request到收到response的时延,这个指标跟吞吐每每是此消彼长的,咱们追求的是必定时延下的高吞吐。后端

好比有100万次请求,99万次请求都在10毫秒内响应,其余次数10秒才响应,平均时延不高,但时延高的用户受不了,因此,就有了TP90/TP99指标,这个指标不是求平均,而是把时延从小到大排序,取排名90%/99%的时延,这个指标越大,对慢请求越敏感。缓存

除此以外,有时候,咱们也会关注可用性指标,这可归到稳定性。安全

通常而言,用户感知友好的高并发系统,时延应该控制在250毫秒之内。性能优化

什么样的系统才能称为高并发?这个很差回答,由于它取决于系统或者业务的类型。不过我能够告诉你一些众所周知的指标,这样能帮助你下次在跟人扯淡的时候稍微靠点儿谱,不至于贻笑大方。服务器

一般,数据库单机每秒也就能抗住几千这个量级,而作逻辑处理的服务单台每秒抗几万、甚至几十万都有可能,而消息队列等中间件单机每秒处理个几万没问题,因此咱们常常听到每秒处理数百万、数千万的消息中间件集群,而像阿某的API网关,每日百亿请求也有可能。

二、高并发的设计思路

高并发的设计思路有两个方向:

  1. 垂直方向扩展,也叫竖向扩展
  2. 水平方向扩展,也叫横向扩展

垂直方向:提高单机能力

提高单机处理能力又可分为硬件和软件两个方面:

  • 硬件方向,很好理解,花钱升级机器,更多核更高主频更大存储空间更多带宽
  • 软件方向,包括用各快的数据结构,改进架构,应用多线程、协程,以及上性能优化各类手段,但这玩意儿天花板低,就像提高我的产出同样,99六、00七、最多24 X 7。

水平方向:分布式集群

为了解决分布式系统的复杂性问题,通常会用到架构分层和服务拆分,经过分层作隔离,经过微服务解耦

这个理论上没有上限,只要作好层次和服务划分,加机器扩容就能知足需求,但实际上并不是如此,一方面分布式会增长系统复杂性,另外一方面集群规模上去以后,也会引入一堆AIOps、服务发现、服务治理的新问题。

由于垂直向的限制,因此,咱们一般更关注水平扩展,高并发系统的实施也主要围绕水平方向展开。

三、高并发的关键技术

玩具式的网络服务程序,用户能够直连服务器,甚至不须要数据库,直接写磁盘文件。但春运购票系统显然不能这么作,它确定扛不住这个压力,那通常的高并发系统是怎么作呢?好比某宝这样的正经系统是怎么处理高并发的呢?

其实大的思路都差很少,层次划分 + 功能划分。能够把层次划分理解为水平方向的划分,而功能划分理解为垂直方向的划分。

首先,用户不能直连服务器,要作分布式就要解决“分”的问题,有多个服务实例就须要作负载均衡,有不一样服务类型就须要服务发现。

集群化:负载均衡

负载均衡就是把负载(request)均衡分配到不一样的服务实例,利用集群的能力去对抗高并发,负载均衡是服务集群化的实施要素,它分3种:

  1. DNS负载均衡,客户端经过URL发起网络服务请求的时候,会去DNS服务器作域名解释,DNS会按必定的策略(好比就近策略)把URL转换成IP地址,同一个URL会被解释成不一样的IP地址,这即是DNS负载均衡,它是一种粗粒度的负载均衡,它只用URL前半部分,由于DNS负载均衡通常采用就近原则,因此一般能下降时延,但DNS有cache,因此也会更新不及时的问题。
  2. 硬件负载均衡,经过布置特殊的负载均衡设备到机房作负载均衡,好比F5,这种设备贵,性能高,能够支撑每秒百万并发,还能作一些安全防御,好比防火墙。
  3. 软件负载均衡,根据工做在ISO 7层网络模型的层次,可分为四层负载均衡(好比章文嵩博士的LVS)和七层负载均衡(NGINX),软件负载均衡配置灵活,扩展性强,阿某云的SLB做为服务对外售卖,Nginx能够对URL的后半部作解释承担API网关的职责。

因此,完整的负载均衡链路是 client <-> DNS负载均衡 -> F5 -> LVS/SLB -> NGINX

无论选择哪一种LB策略,或者组合LB策略,逻辑上,咱们均可以视为负载均衡层,经过添加负载均衡层,咱们将负载均匀分散到了后面的服务集群,具有基础的高并发能力,但这只是万里长征第一步。

数据库层面:分库分表+读写分离

前面经过负载均衡解决了无状态服务的水平扩展问题,但咱们的系统不全是无状态的,后面一般还有有状态的数据库,因此解决了前面的问题,存储有可能成为系统的瓶颈,咱们须要对有状态存储作分片路由

数据库的单机QPS通常不高,也就几千,显然知足不了高并发的要求。

因此,咱们须要作分库分表 + 读写分离。

就是把一个库分红多个库,部署在多个数据库服务上,主库承载写请求,从库承载读请求。从库能够挂载多个,由于不少场景写的请求远少于读的请求,这样就把对单个库的压力降下来了。

若是写的请求上升就继续分库分表,若是读的请求上升就挂更多的从库,但数据库天生不是很适合高并发,并且数据库对机器配置的要求通常很高,致使单位服务成本高,因此,这样加机器抗压力成本过高,还得另外想招。

读多写少:缓存

缓存的理论依据是局部性原理

通常系统的写入请求远少于读请求,针对写少读多的场景,很适合引入缓存集群。

在写数据库的时候同时写一份数据到缓存集群里,而后用缓存集群来承载大部分的读请求,由于缓存集群很容易作到高性能,因此,这样的话,经过缓存集群,就能够用更少的机器资源承载更高的并发。

缓存的命中率通常能作到很高,并且速度很快,处理能力也强(单机很容易作到几万并发),是理想的解决方案。

CDN本质上就是缓存,被用户大量访问的静态资源缓存在CDN中是目前的通用作法。

缓存也有不少须要谨慎处理的问题:

  1. 一致性问题:(a)更新db成功+更新cache失败 -> 不一致 (b)更新db失败+更新cache成功 -> 不一致 ©更新db成功+淘汰缓存失败 -> 不一致
  2. 缓存穿透:查询必定不存在的数据,会穿透缓存直接压到数据库,从而致使缓存失去做用,若是有人利用这个漏洞,大量查询必定不存在的数据,会对数据库形成压力,甚至打挂数据库。解决方案:布隆过滤器 或者 简单的方案,查询不存在的key,也把空结果写入缓存(设置较短的过时淘汰时间),从而下降命失
  3. 缓存雪崩:若是大量缓存在一个时刻同时失效,则请求会转到DB,则对DB造成压迫,致使雪崩。简单的解决方案是为缓存失效时间添加随机值,下降同一时间点失效淘汰缓存数,避免集体失效事件发生

但缓存是针对读,若是写的压力很大,怎么办?

高写入:消息中间件

同理,经过跟主库加机器,耗费的机器资源是很大的,这个就是数据库系统的特色所决定的。

相同的资源下,数据库系统过重太复杂,因此并发承载能力就在几千/s的量级,因此此时你须要引入别的一些技术。

好比说消息中间件技术,也就是MQ集群,它是很是好的作写请求异步化处理,实现削峰填谷的效果。

消息队列能作解耦,在只须要最终一致性的场景下,很适合用来配合作流控。

假如说,每秒是1万次写请求,其中好比5千次请求是必须请求过来立马写入数据库中的,可是另外5千次写请求是能够容许异步化等待个几十秒,甚至几分钟后才落入数据库内的。

那么此时彻底能够引入消息中间件集群,把容许异步化的每秒5千次请求写入MQ,而后基于MQ作一个削峰填谷。好比就以平稳的1000/s的速度消费出来而后落入数据库中便可,此时就会大幅度下降数据库的写入压力。

业界有不少著名的消息中间件,好比ZeroMQ,rabbitMQ,kafka等。

消息队列自己也跟缓存系统同样,能够用不多的资源支撑很高的并发请求,用它来支撑部分容许异步化的高并发写入是很合适的,比使用数据库直接支撑那部分高并发请求要减小不少的机器使用量。

避免挤兑:流控

再强大的系统,也怕流量短事件内集中爆发,就像银行怕挤兑同样,因此,高并发另外一个必不可少的模块就是流控。

流控的关键是流控算法,有4种常见的流控算法。

  1. 计数器算法(固定窗口):计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略,下一个周期开始时,进行清零,从新计数,实现简单。计数器算法方式限流对于周期比较长的限流,存在很大的弊端,有严重的临界问题。
  2. 滑动窗口算法:将时间周期分为N个小周期,分别记录每一个小周期内访问次数,而且根据时间滑动删除过时的小周期,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。此算法能够很好的解决固定窗口算法的临界问题。
  3. 漏桶算法:访问请求到达时直接放入漏桶,如当前容量已达到上限(限流值),则进行丢弃(触发限流策略)。漏桶以固定的速率进行释放访问请求(即请求经过),直到漏桶为空。分布式环境下实施难度高。
  4. 令牌桶算法:程序以r(r=时间周期/限流值)的速度向令牌桶中增长令牌,直到令牌桶满,请求到达时向令牌桶请求令牌,如获取到令牌则经过请求,不然触发限流策略。分布式环境下实施难度高。

四、高并发的实践经验

接入-逻辑-存储是经典的互联网后端分层,但随着业务规模的提升,逻辑层的复杂度也上升了,因此,针对逻辑层的架构设计也出现不少新的技术和思路,常见的作法包括系统拆分,微服务。

除此以外,也有不少业界的优秀实践,包括某信服务器经过协程(无侵入,已开源libco)改造,极大的提升了系统的并发度和稳定性,另外,缓存预热,预计算,批量读写(减小IO),池技术等也普遍应用在实践中,有效的提高了系统并发能力。

为了提高并发能力,逻辑后端对请求的处理,通常会用到生产者-消费者多线程模型,即I/O线程负责网络IO,协议编解码,网络字节流被解码后产生的协议对象,会被包装成task投入到task queue,而后worker线程会从该队列取出task执行,有些系统会用多进程而非多线程,经过共享存储,维护2个方向的shm queue,一个input q,一个output q,为了提升并发度,有时候会引入协程,协程是用户线程态的多执行流,它的切换成本更低,一般有更好的调度效率。

另外,构建漏斗型业务或者系统,从客户端请求到接入层,到逻辑层,到DB层,层层递减,过滤掉请求,Fail Fast(尽早发现尽早过滤),嘴大屁眼小,哈哈。

漏斗型系统不只仅是一个技术模型,它也能够是一个产品思惟,配合产品的用户分流,逻辑分离,能够构建全方位的立体模型。

五、小结

莫让浮云遮望眼,除去繁华识真颜。咱们不能掌握了大方案,吹完了牛皮,而忽视了编程最本质的东西,掌握最基本最核心的编程能力,好比数据架构和算法,设计,惯用法,培养技术的审美,也是很重要的,既要致高远,又要尽精微

 

点击关注,第一时间了解华为云新鲜技术~

相关文章
相关标签/搜索