什么是高并发

高并发指的是系统同时处理很多要求。

高并发是一个结果导向的东西,例如,常见的高并发场景有:淘宝的双11、春运时的抢票、微博大V的热点新闻等,这些范例场景并不是陡然出世,而是随着业务发展的发展而逐渐涌现。
像2020年淘宝双11环球狂欢季,订单创建峰值达到了惊人的58.3万笔/秒,4年前的2016年,这个数字大概是四分之一,再往前四年,这个数据不可考,但是肯定就没这么夸年夜了。

php统计文章浏览量高并发我把握不住啊 Ruby

高并发的业务场景涌现了,随之而来的便是要支持这个高并发业务场景的架构——技能要为业务做事,业务倒逼技能发展。
高并发的架构也不是某个天才冥思苦想或者灵机一动,这个过程是随着业务的发展而演进。
用一个比喻,先有了秋名山,才到了老司机。

img

「那到底多大并发才算高并发呢?」

这个本身是没有详细标准的事情,只看数据是弗成的,要结合详细的场景。
不能说10W QPS的秒杀是高并发,而1W QPS的信息流就不是高并发。
信息流场景涉及繁芜的推举模型和各种人工策略,它的业务逻辑可能比秒杀场景繁芜10倍不止。
业务场景不一样,实行繁芜度不一样,单看并发量也没故意义。

总结便是,高并发无定势,是要和详细的业务场景相结合的。
无高并发场景,无高并发架构。

高并发目标宏不雅观目标

高并发绝不意味着只追求高性能。
从宏不雅观角度看,高并发系统设计的目标有三个:高性能、高可用,以及高可扩展。
便是所谓的“三高”,三高不是伶仃的,而是相互支撑的。

1、高性能:性能表示了系统的并行处理能力,在有限的硬件投入下,提高性能意味着节省本钱。
同时,性能也反响了用户体验,相应韶光分别是100毫秒和1秒,给用户的感想熏染是完备不同的。

2、高可用:表示系统可以正常做事的韶光。
一个整年一直机、无端障;另一个隔三差五出线上事件、宕机,用户肯定选择前者。
其余,如果系统只能做到90%可用,也会大大拖累业务。

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

三高

这3个目标是须要通盘考虑的,由于它们相互干联、乃至也会相互影响。

比如说:考虑系统的扩展能力,你须要将做事设计成无状态的,这种集群设计担保了高扩展性,实在也间接提升了系统的性能和可用性。

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

详细目标性能指标

性能指标通过性能指标可以度量目前存在的性能问题,也是高并发紧张关注的指标,性能和流量方面常用的一些指标有

QPS/TPS/HPS:QPS是每秒查询数,TPS是每秒事务数,HPS是每秒HTTP要求数。
最常用的指标是QPS。

须要把稳的是,并发数和QPS是不同的观点,并发数是指系统同时能处理的要求数量,反应了系统的负载能力。

并发数 = QPS✖均匀相应韶光

相应韶光:从要求发出到收到相应花费的韶光,例如一个别系处理一个HTTP要求须要100ms,这个100ms便是系统的相应韶光。
均匀相应韶光:最常用,但是毛病很明显,对付慢要求不敏感。
比如 1 万次要求,个中 9900 次是 1ms,100 次是 100ms,则均匀相应韶光为 1.99ms,虽然均匀耗时仅增加了 0.99ms,但是 1%要求的相应韶光已经增加了 100 倍。
TP90、TP99 平分位值:将相应韶光按照从小到大排序,TP90 表示排在第 90 分位的相应韶光, 分位值越大,对慢要求越敏感。

img

RPS(吞吐量):单位韶光内处理的要求量,常日由QPS和并发数决定。
常日,设定性能目标时会兼顾吞吐量和相应韶光,比如这样表述:在每秒 1 万次要求下,AVG 掌握在 50ms 以下,TP99 掌握在 100ms 以下。
对付高并发系统,AVG 和 TP 分位值必须同时要考虑。
其余,从用户体验角度来看,200 毫秒被认为是第一个分界点,用户觉得不到延迟,1 秒是第二个分界点,用户能感想熏染到延迟,但是可以接管。
因此,对付一个康健的高并发系统,TP99 该当掌握在 200 毫秒以内,TP999 或者 TP9999 该当掌握在 1 秒以内。
PV:综合浏览量,即页面浏览量或者点击量,一个访客在24小时内访问的页面数量。
UV:独立访客 ,即一定韶光范围内相同访客多次访问网站,只打算为一个独立的访客。
带宽:打算带宽大小须要关注两个指标,峰值流量和页面的均匀大小。
日网站带宽可以利用下面的公式来粗略打算:日网站带宽=pv/统计韶光(换算到秒)均匀页面大小(单位kB)8

峰值一样平常是均匀值的倍数;

QPS不即是并发连接数,QPS是每秒HTTP要求数量,并发连接数是系统同时处理的要求数量:

峰值每秒要求数(QPS) = (总PV数 80%) /(6小时秒数 20%)

可用性指标

高可用性是指系统具有较高的无端障运行能力,可用性 = 均匀故障韶光 / 系统总运行韶光,一样平常利用几个 9 来描述系统的可用性。

可用性指标

对付大多数系统。
2个9是基本可用(如果达不到开拓和运维可能就要被祭天了),3个9是较高可用,4个9是具有自动规复能力的高可用。
要想达到3个9和4个9很困难,可用性影响成分非常多,很难掌握,须要过硬的技能、大量的设备资金投入,工程师要具备任务心,乃至还要点运气。

可扩展性指标

面对突发流量,不可能临时改造架构,最快的办法便是增加机器来线性提高系统的处理能力。

对付业务集群或者根本组件来说,扩展性 = 性能提升比例 / 机器增加比例,空想的扩展能力是:资源增加几倍,性能提升几倍。
常日来说,扩展能力要坚持在 70%以上。

但是从高并发系统的整体架构角度来看,扩展的目标不仅仅是把做事设计成无状态就行了,由于当流量增加 10 倍,业务做事可以快速扩容 10 倍,但是数据库可能就成为了新的瓶颈。

像 MySQL 这种有状态的存储做事常日是扩展的技能难点,如果架构上没提前做好方案(垂直和水平拆分),就会涉及到大量数据的迁移。

我们须要站在整体架构的角度,而不仅仅是业务做事器的角度来考虑系统的扩展性 。
以是说,数据库、缓存、依赖的第三方、负载均衡、交流机带宽等等都是系统扩展时须要考虑的成分。
我们要知 道系统并发到了某一个量级之后,哪一个成分会成为我们的瓶颈点,从而针对性地进行扩展。

高并发架构演进

谁不是生下来便是老司机,架构也不是架起来就支持高并发。
我们来看一个经典的架构演进的例子——淘宝,真实诠释了“好的架构是进化来的,不是设计来的”。

以下是来自《淘宝技能这十年》描述的淘宝2003—2012年的架构演进。

个人网站

初代淘宝的团队职员只有十来个,而且面临千载难逢的商业机会,以是哀求上线的韶光越快越好(实际用了不到一个月),那么淘宝的这些牛人是怎么做到的呢?

——买一个。

初代淘宝买了这样一个架构的网站:LAMP(Linux+Apache+MySQL+PHP)。
全体系统的架构如下:

初代架构

末了开拓的网站是这样的:

初代淘宝网站

由于商品搜索比较占用数据库资源,后来还引入了阿里巴巴的搜索引擎iSearch。

Oracle/支付宝/旺旺

淘宝飞速发展,流量和交易量迅速提升,给技能带来了新的问题——MySQL抗不住了。
怎么办?要搞点事情吗?没有,淘宝买了Oracle数据库,当然这个也考虑到团队里有Oracle大牛的缘故原由。

更换了数据库之后的架构:

引入Oracle之后的淘宝架构

比较故意思的,当时由于买不起商用的连接池,以是用了一个开源的连接池代理做事SQLRelay,这个代理做事常常会去世锁,怎么办理呢?人肉运维,工程师24小时待命,涌现问题赶紧重启SQL Relay做事。

后来为了优化存储,又买了NAS(Network Attached Storage,网络附属存储),NetApp 的 NAS 存储作为了数据库的存储设备,加上 Oracle RAC(Real Application Clusters,实时运用集群)来实现负载均衡。

Java 时期 1.0

2004年,淘宝已经运行了一年的韶光,上面提到的SQLRelay的问题办理不了,数据库必须要用Oracle,以是决定改换开拓措辞。

在不拖慢现有业务发展的情形下,平滑改换整体的架构,对当时的淘宝仍旧是个有寻衅性的事情。
以是怎么办?淘宝的办理方案是请了Sun公司的大佬。

当时,由于struts1.x存在很多问题,以是淘宝自研了一套MVC框架。
Sun当时在推EJB,以是这套架构里也引入了EJB。

Java 时期 1.0

Java 时期 2.0

在之前,淘宝的架构的架构紧张思路还是“买”,随着业务的发展,到了2005 年,“买”已经很难办理问题了,须要对全体架构进行调度和优化,须要综合考虑容量、性能、本钱的问题。

在Java时期2.0,紧张做了对数据分库、放弃EJB、引入Spring、加入缓存、加入CDN等。

Java时期2.0

Java时期3.0

Java时期3.0的最大特点便是淘宝开始从商用转为“自研”,开始真正创造自己的核心技能,例如缓存存储引擎Tair,分布式存储系统TFS。
搜索引擎iSearch也进行了升级。
引入了自研技能的淘宝架构:

Java时期3.0

分布式时期1.0

到了2008年的时候,淘宝的业务进一步发展。

全体主站系统的容量已经到了瓶颈,商品数在1亿个以上,PV在2.5亿个以上,会员数超过了 5000万个。
这时Oracle的连接池数量都不足用了,数据库的容量到了极限,纵然上层系统加机器也无法连续扩容,我们只有把底层的根本做事连续拆分,从底层开始扩容,上层才能扩展,这才能容纳往后三五年的增长。

淘宝开始对业务模块逐步拆分和做事化改造。
例如拆分出了商品中央、商品中央等等。
同时引入了一些自研的中间件,如分布式数据库中间件,分布式中间件等等。

面向做事架构

《淘宝技能这十年》这本书只描述到了2012年,也便是分布式时期。
上图是根据参考【8】画的一张图。

转眼理2012又快过了十年,这十年,阿里巴巴逐渐进入极盛时期,技能上也是风起云涌,秀士辈出。
粒度更细的微做事、隔离差距的容器化技能、快速伸缩的云平台技能…… 如果《淘宝技能这十年》的作者能再写一个十年,一定也是非常精彩。

按照参考【10】,接下来的淘宝做事化开始逐渐演进到云平台架构,由于资料实在难找,而且这时候以淘宝的体量,内部的架构繁芜度足以写一本书了。
以是接下来的架构演进参考做事端高并发分布式架构演进之路,是一个牛人以淘宝为仿照工具进行的架构演进,虽然不是淘宝真正的架构技能演进,但也很值得借鉴。

在这里我们略过了微做事架构——分布式时期2.0,微做事本身是更细粒度、更轻量级的做事化,这里插入一个关于微做事很故意思的说法——马丁老哥老被人说设计的东西不符合面向做事的观点,于是他就自己发明创造了一个灵巧的微做事理论,往后再有人说:马老师,你又不遵照微做事架构设计的原则了。
嗯,你说哪一点不符合,我立马去改微做事的理论。

容器化时期

前最盛行的容器化技能是Docker,最盛行的容器管理做事是Kubernetes(K8S),运用/做事可以打包为Docker镜像,通过K8S来动态分发和支配镜像。
Docker镜像可理解为一个能运行你的运用/做事的最小的操作系统,里面放着运用/做事的运行代码,运行环境根据实际的须要设置好。
把全体“操作系统”打包为一个镜像后,就可以分发到须要支配干系做事的机器上,直接启动Docker镜像就可以把做事起起来,使做事的支配和运维变得大略。

在大匆匆的之前,可以在现有的机器集群上划分出做事器来启动Docker镜像,增强做事的性能,大匆匆过后就可以关闭镜像,对机器上的其他做事不造成影响。

容器化时期

云平台时期

在做事化的时候,淘宝已经演进到了云平台架构。

所谓的云平台,便是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上供应通用的操作系统,供应常用的技能组件(如Hadoop技能栈,MPP数据库等)供用户利用,乃至供应开拓好的运用,用户不须要关系运用内部利用了什么技能,就能够办理需求(如音视频转码做事、邮件做事、个人博客等)。

clipboard.png

「大略总结一下」:高并发的架构某种程度上是逼出来的,一样平常人谁能想到淘宝当年抛弃php是由于办理不了数据库连接池的问题。
架构演进就像是西湖的水——西湖的水,工程师的泪,提及来随意马虎,里面究竟灭了多少火,填了多少坑。
我们外人看到的平湖秋波,里面水很深。

高并发架构实现

想让系统抗住更多的并发,紧张便是两个方向:

「纵向扩展」:1、提升单机的硬件性能:通过增加内存、 CPU核数、存储容量、或者将磁盘 升级成SSD等堆硬件的办法来提升2、提升单机的软件性能:利用缓存减少IO次数,利用并发或者异步的办法增加吞吐量。
「横向扩展」:单机性能总会存在极限,以是终极还须要引入横向扩展,通过集群支配以进一步提高并发处理能力。
1、做好分层架构:这是横向扩展的条件,由于高并发系统每每业务繁芜,通过分层处理可以简化繁芜问题,更随意马虎做到横向扩展。
2、各层进行水平扩展:无状态水平扩容,有状态做分片路由。
业务集群常日能设计成无状态的,而数据库和缓存每每是有状态的,因此须要设计分区键做好存储分片,当然也可以通过主从同步、读写分离的方案提升读性能。

用一个比喻,你要去打十个大汉,你大概是打不过的,最好的结果便是他们打不倒你——吊起来打。
以是这时候就得想办法了。
第一个办法便是努力磨炼,然后全副武装,大概还有点希望,这便是纵向扩展;第二个办法,弗成,你一看对面人多,你就叫了十九个兄弟,然后你们二十个打他们十个,唉,这下看上去能打的过了,这便是横向扩展;还有第三个不常用的办法,你找个门把住,每次就放一个大汉进来,打倒一个再放下一个,这个便是削峰限流的做法。

我要打十个

我们看一下一个大概的支持三高的范例架构:

高并发范例架构

接下来,我们从上往下,看一下,各层的一些关键技能。

网络层多机器

「堆机器不是万能的,不堆机器是切切不能的。

我们努力地升级改造架构,末了让我们供应的做事能够快速横向扩展。
横向扩展的根本同样是要有一定数量的、一定性能的机器。

还是上面哪个比喻,你要打十个大汉,等你努力练成了叶师傅,你溘然创造对面的孩子都终年夜了,人数×2,这时候你还是得叫兄弟。

一样平常狗大户大厂在全国各地都有机房,可能光北京就有两个,把不同地方的要求分到不同的机房,再分到不同的集群,再分到不同的机器,这么一匀,就在做事能扛的范畴之内了。
我们大概来看一下,怎么估算所需机器的数量。

通过QPS和PV打算支配做事器的台数

单台做事器每天PV打算:

公式1:每天总PV = QPS 3600 6公式2:每天总PV = QPS 3600 8

做事器打算:

做事器数量 = ceil( 每天总PV / 单台做事器每天总PV )

峰值QPS和机器打算公式

事理:每天80%的访问集中在20%的韶光里,这20%韶光叫做峰值韶光

公式:( 总PV数 80% ) / ( 每天秒数 20% ) = 峰值韶光每秒要求数(QPS)

机器:峰值韶光每秒QPS / 单台机器的QPS = 须要的机器。

一样平常有大流量业务的公司都实现了多机房,包括同城多机房、跨城多机房、跨国多机房等。
为了担保可用性,财大气粗的公司会预备大量的冗余,一样平常会担保机器数是打算峰值所需机器数的两倍。
须要节约本钱的,也可以考虑当前盛行的云平台,之前热点事宜的时候,微博就从阿里云租了不少云做事器。

DNS

DNS大略示意图

DNS是要求分发的第一个关口,实现的是地理级别的均衡。
dns-server对一个域名配置了多个解析ip,每次DNS解析要求来访问dns-server。
常日会返回离用户间隔比较近的ip,用户再去访问ip。
例如,北京的用户访问北京的机房,南京的用户访问南京的资源。

一样平常不会利用DNS来做机器级别的负载均衡,由于造不起,IP资源实在太宝贵了,例如百度搜索可能须要数万台机器,不可能给每个机器都配置公网IP。
一样平常只会有有限的公网IP的节点,然后再在这些节点上做机器级别的负载均衡,这样各个机房的机器只须要配置局域网IP就行了。

DNS负载均衡的优点是通用(环球通用)、本钱低(申请域名,注册DNS即可)。

缺陷也比较明显,紧张表示在:

DNS 缓存的韶光比较长,纵然将某台业务机器从 DNS 做事器上删除,由于缓存的缘故原由,还是有很多用户会连续访问已经被删除的机器。
DNS 不足灵巧。
DNS 不能感知后端做事器的状态,只能根据配置策略进行负载均衡,无法做到更加灵巧的负载均衡策略。
比如说某台机器的配置比其他机器要好很多,理论上来说该当多分配一些要求给它,但 DNS 无法做到这一点。

以是对付时延和故障敏感的业务,有实力的公司可能会考试测验实现HTTP-DNS的功能,即利用HTTP 协议实现一个私有的 DNS 系统。
HTTP-DNS 紧张运用在通过 App 供应做事的业务上,由于在 App 端可以实现灵巧的做事器访问策略,如果是 Web 业务,实现起来就比较麻烦一些,由于 URL 的解析是由浏览器来完成的,只有 Javascript 的访问可以像 App 那样实现比较灵巧的掌握。

CDN

CDN是为理解决用户网络访问时的“末了一公里”效应,实质是一种“以空间换韶光”的加速策略,即将内容缓存在离用户最近的地方,用户访问的是缓存的内容,而不是站点实时访问的内容。

由于CDN支配在网络运营商的机房,这些运营商又是终端用户的网络供应商,因此用户要求路由的第一跳就到达了CDN做事器,当CDN中存在浏览器要求的资源时,从CDN直接返回给浏览器,最短路径返回相应,加快用户访问速率。

下面是大略的CDN要求流程示意图:

CDN要求流程图

CDN能够缓存的一样平常是静态资源,如图片、文件、CSS、Script脚本、静态网页等,但是这些文件访问频度很高,将其缓存在CDN可极大改进网页的打开速率。

反向代理层

我们把这一层叫反向代理层,也可以叫接入层、或者负载层。
这一层是流量的入口,是系统抗并发很关键的一层。

还是那个比喻,还是你打十个大汉,这次你叫了十九个兄弟,空想的情形是你们两个打对面一个,但是你由于太激动,冲在了最前面,结果瞬间被十个大汉暴打……

反向代理会对流量进行分发,担保终极落到每个做事上的流量是做事能扛的范围之内。

Nginx、LVS、F5

DNS 用于实现地理级别的负载均衡,而 Nginx、 LVS、 F5 用于同一地点内机器级别的负载均衡。
个中 Nginx 是软件的 7 层负载均衡,LVS 是内核的 4 层负载均衡,F5 是硬件的 4 层负载均衡。

软件和硬件的差异就在于性能,硬件远远高于软件,Ngxin 的性能是万级,一样平常的 Linux 做事器上装个 Nginx 大概能到 5 万 / 秒;LVS 的性能是十万级,听说可达到 80万 / 秒;F5 性能是百万级,从 200 万 / 秒到 800 万 / 秒都有。

硬件虽然性能高,但是单台硬件的本钱也很高,一台最便宜的 F5 都是几十万,但是如果按照同等要求量级来打算本钱的话,实际上硬件负载均衡设备可能会更便宜,例如假设每秒处理 100 万要求,用一台 F5 就够了,但用 Nginx, 可能要 20 台,这样折算下来用 F5 的本钱反而低。
因此常日情形下,如果性能哀求不高,可以用软件负载均衡;如果性能哀求很髙,推举用硬件负载均衡。

4 层和 7 层的差异就在于协议和灵巧性。
Nginx 支持 HTTP、 E-mail 协议,而 LVS 和 F5 是 4层负载均衡,和协议无关,险些所有运用都可以做,例如谈天、数据库等。
目前很多云做事商都已经供应了负载均衡的产品,例如阿里云的 SLB、UCIoud 的 ULB 等,中小公司直接购买即可。

对付开拓而言,一样平常只须要关注到Nginx这一层面就行了。

Nginx负载均衡架构示意图

负载均衡范例架构

像上面提到的负载均衡机制,在利用中,可以组合利用。

DNS负载均衡用于实现地理级别的负载均衡,硬件件负载均衡用于实现集群级别的负载均衡;软件负载均衡用于实现机器级别的负载均衡。

三层负载均衡

全体系统的负载均衡分为三层。

地理级别负载均衡:www.xxx.com 支配在北京、广州、上海三个机房,当用户访问时,DNS 会根据用户的地理位置来决定返回哪个机房的 IP,图中返回了广州机房的 IP 地址,这样用户就访问到广州机房了。
集群级别负载均衡:广州机房的负载均衡用的是 F5 设备,F5 收到用户要求后,进行集群级别的负载均衡,将用户要求发给 3 个本地集群中的一个,我们假设 F5 将用户要求发给了 “广州集群 2” 。
机器级别的负载均衡:广州集群 2 的负载均衡用的是 Nginx, Nginx 收到用户要求后,将用户要求发送给集群里面的某台做事器,做事器处理用户的业务要求并返回业务相应。
Nginx负载均衡

我们紧张关心是Nginx这一层的负载,常日LVS 和 F5这两层都是由网络运维工程师管控。

Nginx负载均衡/反向代理示意图

对付负载均衡我们紧张关心的几个方面如下:

上游做事器配置:利用 upstream server配置上游做事器负载均衡算法:配置多个上游做事器时的负载均衡机制。
失落败重试机制:配置当超时或上游做事器不存活时,是否须要重试其他上游做事器。
做事器心跳检讨:上游做事器的康健检讨/心跳检讨。

upstream server中文直接翻译是上游做事器,意思便是负载均衡做事器设置,便是被nginx代理末了真实访问的做事器。

负载均衡算法

负载均衡算法数量较多,Nginx紧张支持以下几种负载均衡算法:

1、轮询(默认)

每个要求按韶光顺序逐一分配到不同的后端做事,如果后端某台做事器去世机,自动剔除故障系统,利用户访问不受影响。

2、weight(轮询权值)

weight的值越大分配到的访问概率越高,紧张用于后端每台做事器性能不屈衡的情形下。
或者仅仅为在主从的情形下设置不同的权值,达到合理有效的地利用主机资源。

3、ip_hash

每个要求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端做事器,并且可以有效办理动态网页存在的session共享问题。

4、fair

比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载韶光是非智能地进行负载均衡,也便是根据后端做事器的相应韶光 来分配要求,相应韶光短的优先分配。
Nginx本身不支持fair,如果须要这种调度算法,则必须安装upstream_fair模块。

5、url_hash

按访问的URL的哈希结果来分配要求,使每个URL定向到一台后端做事器,可以进一步提高后端缓存做事器的效率。
Nginx本身不支持url_hash,如果须要这种调度算法,则必须安装Nginx的hash软件包。

失落败重试

Nginx关于失落败重试紧张有两部分配置,upstream server 和 proxy_pass。

通过配置上游做事器的 max_fails和 fail_timeout,来指定每个上游做事器,当fail_timeout韶光内失落败了max_fail次要求,则认为该上游做事器不可用/不存活,然后将会摘掉该上游做事器,fail_timeout韶光后会再次将该做事器加入到存活上游做事器列表进行重试。

康健检讨

Nginx 对上游做事器的康健检讨默认采取的是惰性策略,Nginx 商业版供应了healthcheck 进 行 主 动 健 康 检 查 。
当 然 也 可 以 集 成 nginx_upstream_check_module( https://github.com/yaoweibin/nginx_upstream_check module ) 模块来进行主动康健检讨。

nginx_upstream_check_module 支持 TCP 心跳和 HTTP 心跳来实现康健检讨。

流量掌握流量分发

流量分发就不多说了,上面已经讲了,是接入层的基本功能。

流量切换

我听朋友说过一个故意思的事情,他们公司将流量从一个机房切到另一个机房,结果翻车,所有工程师运维平台一片飘红,全公司集体围不雅观,运维团队就很丢面子。

幸灾乐祸

流量切换便是在某些情形下,比如机房故障、光纤被挖断、做事器故障故障情形,或者灰度发布、A/B等运维测试场景,须要将流量切到不同的机房、做事器等等。

就像我们上面提到的负载均衡范例架构,不同层级的负载卖力切换不同层级的流量。

「DNS」:切换机房入口。
「HttpDNS」:紧张 「APP」 场景下,在客户端分配好流量入口,绕过运营商 「LocalDNS」并实现更精准流量调度。
「LVS」/「HaProxy」:切换故障的 「Nginx」 接入层。
「Nginx」:切换故障的运用层。

其余,有些运用为了更方便切换,还可以在 「Nginx」 接入层做切换,通过 「Nginx」 进行一些流量切换,而没有通过如 「LVS」/「HaProxy」 做切换。

限流

限流是担保系统可用的一个主要手段,防止超负荷的流量直接打在做事上,限流算法紧张有令牌桶、漏桶。

漏桶和令牌桶

可以在很多层面做限流,例如做事层网关限流、行列步队限流、Redis限流,这些紧张是业务上的限流。

这里我们紧张谈论的是接入层的限流,直接在流量入口上限流。

对付 Nginx接入层限流可以利用 Nginx自带的两个模块:连接数限流模块 ngx_http_limit_conn_module和漏桶算法实现的要求限流模块 ngx_http_limit_req_moduleo

还可以利用 「OpenResty」供应的 「Lua」限流模块 ua-resty-limit-traffic应对更繁芜的限流场景。

limmit_conn用来对某个 key 对应的总的网络连接数进行限流,可以按照如 IP、域名维度进行限流。
limit_req用来对某个 key对应的要求的均匀速率进行限流,有两种用法:平滑模式(「delay」 ) 和许可突发模式(「nodelay」 )。

流量过滤

很多时候,一个网站有很多流量是爬虫流量,或者直接是恶意的流量。

可以在接入层,对要求的参数进行校验,如果参数校验不合法,则直接谢绝要求,或者把要求打到专门用来处理造孽要求的做事。

最大略的是利用Nginx,实际场景可能会利用OpenResty,对爬虫 user-agent 过滤和一些恶意IP (通过统计 IP 访问量来配置阈值),将它们分流到固定分组,这种情形会存在一定程度的误杀,由于公司的公网 IP —般情形下是同一个,大家利用同一个公网出口 IP 访问网站,因此,可以考虑 IP+Cookie 的办法,在用户浏览器栽种标识用户身份的唯一 Cookie。
访问做事前先栽种 Cookie, 访问做事时验证该 Cookie, 如果没有或者禁绝确,则可以考虑分流到固定分组,或者提示输入验证码后访问。

流量过滤

降级

降级也是担保高可用的一把利剑,降级的思路是“弃车保帅”,在眼看着不能担保全局可用的情形下,抛弃或者限定一些不主要的做事。

降级一样平常分为多个层级,例如在运用层进行降级,通过配置中央设置降级的阈值,一旦达到阈值,根据不同的降级策略进行降级。

也可以把降级开关前置到接入层,在接入层配置功能降级开拓,然后根据情形行自动/人工降级。
后端运用做事出问题时,通过接入层降级,可以避免无谓的流量再打到后端做事,从而给运用做事有足够的韶光规复做事。

Web层

经由一系列的负载均衡,用户终于要求到了web层的做事。
web做事开拓完成,经由支配,运行在web做事器中给用户供应做事。

集群

一样平常会根据业务模块,来划分不同的做事,一个做事支配多个实例组成集群。

集群

为了隔离故障,可以再将集群进行分组,这样一个分组涌现问题,也不会影响其它分组。
像比较常问的秒杀,常日会将秒杀的做事集群和普通的做事集群进行隔离。

能做到集群化支配的三个要点是无状态、拆分、做事化。

无状态:设计的运用是无状态的,那么运用比较随意马虎进行水平扩展。
拆分:设计初期可以不用拆分,但是后期访问量大的时候,就可以考虑按功能拆分系统。
拆分的维度也比较灵巧,根据实际情形来选择,例如根据系统维度、功能维度、读写维度、AOP 维度、模块维度等等。
做事化:拆分更多的是设计,做事化是落地,做事化一样平常都得做事管理的问题。
除了最基本的远程调用,还得考虑负载均衡、做事创造、做事隔离、做事限流、做事访问黑白名单等。
乃至还有细节须要考虑,如超时时间、重试机制、做事路由、故障补偿等。
Web做事器

独立开拓一个成熟的 Web 做事器,本钱非常高,况且业界又有那么多成熟的开源 Web 做事器,以是互联网行业基本上都是 "拿来主义" ,挑选一个盛行的开源做事器即可。
大一点的公司,可能会在开源做事器的根本上,结合自己的业务特点做二次开拓,例如淘宝的 Tengine,但一样平常公司基本上只须要将开源做事器摸透,优化一下参数,调度一下配置就差不多了。

做事器的选择紧张和开拓措辞干系,例如,Java 的有 Tomcat、JBoss、Resin 等,PHP/Python 的用 Nginx。

Web做事器的性能之类的一样平常不会成为瓶颈,例如Java最盛行的Web做事器Tomcat默认配置的最大要求数是 150,但是没有关系,集群支配就行了。

容器

容器是最近几年才开始火起来的,个中以 Docker 为代表,在 BAT 级别的公司已经有较多的运用。

容器化可以说给运维带来了革命性的变革。
Docker 启动快,险些不占资源,随时启动和停滞,基于Docker 打造自动化运维、智能化运维逐渐成为主流办法。

容器化技能也天生适宜当前盛行的微做事,容器将微做事进程和运用程序隔离到更小的实例里,利用更少的资源,更快捷地支配。
结合容器编排技能,可以更方便快速地搭建做事高可用集群。

做事层开拓框架

一样平常,互联网公司都会指定一个大的技能方向,然后利用统一的开拓框架。
例如,Java 干系的开拓框架 SSH、SpringBoot, Ruby 的 Ruby on Rails, PHP 的 ThinkPHP, Python 的Django 等。

框架的选择,有一个总的原则:优选成熟的框架,避免盲目追逐新技能!

对付一样平常的螺丝工而言,所做的紧张事情都是在这个开拓框架之下。
对付开拓措辞和框架的利用,一定要充分理解和利用措辞和框架的特性。

以Java为例,在作者的开拓中,涉及到一个加密解密的做事调用,做事供应方利用了JNI的技能——大略说便是C措辞编写代码,供应api供Java调用,填补了Java相对没那么底层的劣势,大大提高了运算的速率。

在做事开拓这个日常事情的层面,可以做到这些事情来提高性能:

并发处理,通过多线程将串行逻辑并行化。
减少IO次数,比如数据库和缓存的批量读写、RPC的批量接口支持、或者通过冗余数据的办法干掉RPC调用。
减少IO时的数据包大小,包括采取轻量级的通信协议、得当的数据构造、去掉接口中的多余字段、减少缓存key的大小、压缩缓存value等。
程序逻辑优化,比如将大概率阻断实行流程的判断逻辑前置、For循环的打算逻辑优化,或者采取更高效的算法各种池化技能的利用和池大小的设置,包括HTTP要求池、线程池(考虑CPU密集型还是IO密集型设置核心参数)、数据库和Redis连接池等。
JVM优化,包括新生代和老年代的大小、GC算法的选择等,尽可能减少GC频率和耗时。
锁选择,读多写少的场景用乐不雅观锁,或者考虑通过分段锁的办法减少锁冲突。

可以通过这些事情来提高可用性:

设置得当的超时时间、重试次数及机制,必要时要及时降级,返回兜底数据等,防止把做事提方供打崩防重设计:通过防重key、防重表等办法实现防重幂等设计:在接口层面实现幂等设计做事中央

当系统数量不多的时候,系统间的调用一样平常都是直接通过配置文件记录在各系统内部的,但当系统数量多了往后,这种办法就存在问题了。

比如说统共有 10 个别系依赖 A 系统的 X 接口,A 系统实现了一个新接口 Y, 能够更好地供应原有 X 接口的功能,如果要让已有的 10 个别系都切换到 Y 接口,则这 10 个别系的几十上百台器的配置都要修正,然后重启,可想而知这个效率是很低的。

做事中央的实现紧张采取做事名字系统。

做事务名字系统 (Service Name System)

看到这个翻译,相信你会急速遐想到 DNS, 即 Domain Name System。
没错,两者的性子是基本类似的。

DNS 的浸染将域名解析为 IP 地址,紧张缘故原由是我们记不住太多的数字 IP, 域名就随意马虎记住。
做事名字系统是为了将 Service 名称解析为 "host + port + 接口名称" ,但是和 DNS一样,真正发起要求的还是要求方。

image-20210501154424095

在微做事的架构下,实现这个功能的称之为注册中央,例如在Java措辞体系下,开源的注册中央有Nacos、Ecuraka等。

配置中央

配置中央便是集中管理各个做事的配置。

在做事不多的时候,各个做事各自管理自己的配置,没有问题,但是当做事成百上千,再各行其政,便是一个比较头疼的事。

以是将配置中央抽象成公共的组件,集中配置多个别系,操作效率高。

在微做事架构体系下,配置中央的开源方案有SpringCloud的SpringCloud Config、阿里的Nacos等。

做事框架

做事拆分最直接的影响便是本地调用的做事变成了远程调用,做事消费者A须要通过注册中央去查询做事供应者B的地址,然后发起调用,这个看似大略的过程就可能会碰着下面几种情形,比如:

注册中央宕机;做事供应者B有节点宕机;做事消费者A和注册中央之间的网络不通;做事供应者B和注册中央之间的网络不通;做事消费者A和做事供应者B之间的网络不通;做事供应者B有些节点性能变慢;做事供应者B短韶光内涌现问题。

怎么去担保做事消费者成功调用做事生产者?这便是做事管理框架要办理的问题。

在Java措辞体系下,目前盛行的做事管理框架有SpringCloud和Dubbo。

以SpringCloud为例:

SpringCloud体系

Feign封装RestTemplate实现http要求办法的远程调用Feign封装Ribbon实现客户端负载均衡Euraka集群支配实现注册中央高可用注册中央心跳监测,更新做事可用状态集成Hystrix实现熔断机制Zuul作为API 网关 ,供应路由转发、要求过滤等功能Config实现分布式配置管理Sluth实现调用链路跟踪集成ELK,通过Kafka行列步队将日志异步写入Elasticsearch,通过Kibana可视化查看

SpringCloud是一整套完全微做事办理方案,被称为“SpringCloud 百口桶”。
这里只是大略地先容一下。

Dubbo紧张供应了最根本的RPC功能。

不过SpringCloud的RPC采取了HTTP协议,可能性能会差一些。

利好的是,“SpringCloud2.0”——SpringCloud Alibaba盛行了起来,Dubbo也可以完美地融入SpringCloud的生态。

行列步队

行列步队在高性能、高扩展、高可用的架构中扮演着很主要的角色。

行列步队是用来解耦一些不须要同步调用的做事或者订阅一些自己系统关心的变革。
利用行列步队可以实现做事解耦(一对多消费)、异步处理、流量削峰/缓冲等。

做事解耦

「做事解耦可以降落做事间耦合,提高系统系统的扩展性。

例如一个订单做事,有多个下贱,如果不用行列步队,那么订单做事就要调用多个下贱。
如果需求要再加下贱,那么订单做事就得添加调用新下流的功能,这就比较烦。

引入行列步队之后,订单做事就可以直接把订单干系塞到行列步队中,下贱系统只管订阅就行了。

做事构造

异步处理

「异步处理可以降落相应韶光,提高系统性能。

随着业务的发展项目的要求链路越来越长,这样一来导致的后果便是相应韶光变长,有些操作实在不用同步处理,这时候就可以考虑采取异步的办法了。

图片

流量削峰/缓冲

「流量削峰/缓冲可以提高系统的可用性。

我们前面提到了接入层的限流,在做事层的限流可以通过行列步队来实现。
网关的要求先放入行列步队中,后端做事尽可能去行列步队中消费要求。
超时的要求可以直接返回缺点,也可以在行列步队中等待。

流量削峰/缓冲

行列步队系统基本功能的实现比较大略,但要做到高性能、高可用、时序性、事务性则比较难。
业界已经有很多成熟的开源实现方案,如果哀求不高,基本上拿来用即可,例如,RocketMQ、Kafka、ActiveMQ 等。

但如果业务对的可靠性、时序、事务性哀求较高时,则要深入研究这些开源方案,提前考虑可能会碰着的问题,例如重复消费、丢失、堆积等等。

平台层

当业务规模比较小、系统繁芜度不高时,运维、测试、数据剖析、管理等支撑功能紧张由各系统或者团队独立完成。
随着业务规模越来越大,系统繁芜度越来越高,子系统数量越来越多,如果连续采纳各不相谋的办法来实现这些支撑功能,会创造重复事情非常多。
以是就会自然地把干系功能抽离出来,作为公共的做事,避免重复造轮子,减少不规范带来的沟通和协作本钱。

平台层是做事化思维下的产物。
将公共的一些功能拆分出来,让干系的业务做事只专注于自己的业务,这样有利于明确做事的职责,方便做事扩展。

同时一些公共的平台,也有利于各个做事之间的统筹,例如数据平台,可以对数据进行聚合,某个做事以前须要一些整合一些数据可能要调用多个上游做事,但是引入数据平台往后,只须要从数据平台取数据就可以了,可以降落做事的相应韶光。

运维平台

运维平台核心的职责分为四大块:配置、支配、监控、应急,每个职责对应系统生命周期的一个阶段,如下图所示:

运维平台

支配:紧张卖力将系统发布到线上。
例如,包管理、灰度发布管理、回滚等。
监控:紧张卖力网络系统上线运行后的干系数据并进行监控,以便及时创造问题。
应急:紧张卖力系统出故障后的处理。
例如,停滞程序、下线故障机器、切换 IP 等。

运维平台的核心设计要素是“四化"——标准化、平台化、自动化、可视化。

标准化:要制订运维标准,规范配置管理、支配流程、监控指标、应急能力等,各系统按照运维标准来实现,避免不同的系统不同的处理办法。
平台化:传统的手工运维办法须要投入大量人力,效率低,随意马虎出错,因此须要在运维标准化的根本上,将运维的干系操作都集成到运维平台中,通过运维平台来完成运维事情。
自动化:传统手工运维办法效率低下的一个紧张缘故原由便是要实行大量重复的操作,运维平台可以将这些重复操作固化下来,由系统自动完成。
可视化:运维平台有非常多的数据,如果全部通过人工去查询数据再来判断,则效率很低,可视化的紧张目的便是为了提升数据查看效率。
测试平台

测试平台核心的职责当然便是测试了,包括单元测试、集成测试、接口测试、性能测试等,都可以在测试平台来完成。

测试平台的核心目的是提升测试效率,从而提升产品质量,其设计关键便是自动化。

测试平台架构

数据平台

数据平台的核心职责紧张包括三部分:数据管理、数据剖析和数据运用。
每一部分又包含更多的细分领域,详细的数据平台架构如下图所示:

数据平台

数据管理

数据管理包含数据采集、数据存储、数据访问和数据安全四个核心职责,是数据平台的根本功能。

数据采集:从业务系统搜集各种数据。
例如,日志、用户行为、业务数据等,将这些数据传送到数据平台。
数据存储:将从业务系统采集的数据存储到数据平台,用于后续数据剖析。
数据访问:卖力对外供应各种协议用于读写数据。
例如,SQL、 Hive、 Key-Value 等读写协议。
数据安全:常日情形下数据平台都是多个业务共享的,部分业务敏感数据须要加以保护,防止被其他业务读取乃至修正,因此须要设计数据安全策略来保护数据。
数据剖析

数据剖析包括数据统计、数据挖掘、机器学习、深度学习等几个细分领域。

数据挖掘:数据挖掘这个观点本身含义可以很广,为了与机器学习和深度学习区分开,这里的数据挖掘紧张是指传统的数据挖掘办法。
例如,有履历的数据剖析职员基于数据仓库构建一系列规则来对数据进行剖析从而创造一些隐含的规律、征象、问题等,经典的数据挖掘案例便是沃尔玛的啤酒与尿布的关联关系的创造。
机器学习、深度学习:机器学习和深度学习属于数据挖掘的一种详细实现办法,由于实在现办法与传统的数据挖掘办法差异较大,因此数据平台在实现机器学习和深度学习时,须要针对机器学习和深度学习独立进行设计。
数据运用

数据运用很广泛,既包括在线业务,也包括离线业务。
例如,推举、广告等属于在线运用,报表、敲诈检测、非常检测等属于离线运用。
数据运用能够发挥代价的条件是须要有 "大数据" ,只有当数据的规模达到一定程度,基于数据的剖析、挖掘才能创造有代价的规律、征象、问题等。
如果数据没有达到一定规模,常日情形下做好数据统计就足够了,尤其是很多初创企业,无须一开始就参考 BAT 来构建自己的数据平台。

管理平台

管理平台的核心职责便是权限管理,无论是业务系统(例如,淘宝网) 、中间件系统(例如,行列步队 Kafka) , 还是平台系统(例如,运维平台) ,都须要进行管理。
如果每个别系都自己来实现权限管理,效率太低,重复事情很多,因此须要统一的管理平台来管理所有的系统的权限。

说到“平台”,不由地想起这几年一下子被人猛吹,一下子被人唱衰的“中台”。
在平台里的数据平台,实在已经和所谓的“数据中台”类似了。
“中台”是个观点性的东西,详细怎么实现,没有统一的标准方案。
作者所在的公司,也跟风建了中台,以“数据中台”为例,我们数据中台的培植紧张为了数据共享和数据可视化,大略说便是把各个业务模块的一些数据汇聚起来。
提及来大略,落地很难,数据汇聚的及时性、数据共享的快速相应……终极的办理方案是采购了阿里的一些商业化组件,花了老鼻子钱,但是效果,不能说一地鸡毛,也差不多吧。

缓存层

虽然我们可以通过各种手段来提升存储系统的性能,但在某些繁芜的业务场景下,纯挚依赖存储系统的性能提升不足的。

绝大部分在线业务都是读多写少。
例如,微博、淘宝、微信这类互联网业务,读业务占了整体业务量的 90%以上。
以微博为例:一个明星发一条微博,可能几千万人来浏览。

如果直接从DB中取数据,有两个问题,一个是DB查询的速率有瓶颈,会增加系统的相应韶光,一个是数据库本身的并发瓶颈。
缓存便是为了填补读多写少场景下存储系统的不敷。

在前面我们提到的CDN可以说是缓存的一种,它缓存的是静态资源。

从全体架构来看,一样平常采取多级缓存的架构,在不同层级对数据进行缓存,来提升访问效率。

多级缓存

大略说一下整体架构和流程,缓存一级一级地去读取,没有命中再去读取下一级,先读取本地缓存,再去读取分布式缓存,分布式缓存也没有命中,末了就得去读取DB。

分布式缓存

为了提高缓存的可用性,一样平常采取分布式缓存。
分布式缓存一样平常采取分片实现,即将数据分散到多个实例或多台做事器。
算法一样平常釆用取模和同等性哈希。

要采取不过期缓存机制,可以考虑取模机制,扩容时一样平常是新建一个集群。

取模算法

而对付可以丢失的缓存数据,可以考虑同等性哈希,纵然个中一个实例出问题只是丢一小部分。

同等性hash算法

对付分片实现可以考虑客户端实现,或者利用如Twemproxy 中间件进行代理(分片对客户端是透明的)。

如果利用 Redis, 则 可 以考虑利用 redis-cluster 分布式集群方案。

Redis Cluster

热点本地缓存

对付那些访问非常频繁的热点缓存,如果每次都去远程缓存系统中获取,可能会由于访问量太大导致远程缓存系统要求过多、负载过高或者带宽过高档问题,终极可能导致缓存相应慢,使客户端要求超时。

一种办理方案是通过挂更多的从缓存,客户端通过负载均衡机制读取从缓存系统数据。
不过也可以在客户端所在的运用/代理层本地存储一份,从而避免访问远程缓存,纵然像库存这种数据,在有些运用系统中也可以进行几秒钟确当地缓存,从而降落远程系统的压力。

缓存的引入虽然提高了系统的性能,但同时也增加了系统的繁芜度,带来了一些运维的本钱。

缓存穿透

缓存穿透是指缓存没有发挥浸染,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统须要再次去存储系统查询数据,结果存储系统也没有数据。

缓存穿透的示意图:

缓存穿透

一样平常情形下,如果存储系统中没有某个数据,则不会在缓存中存储相应的数据,这样就导致用户查询的时候,在缓存中找不到对应的数据,每次都要去存储系统中再查询一遍,然后返回数据不存在。
缓存在这个场景中并没有起到分担存储系统访问压力的浸染。

常日情形下,业务上读取不存在的数据的要求量并不会太大,但如果涌现一些非常情形,例如被黑客攻击,故意大量访问某些读取不存在数据的业务,有可能会将存储系统拖垮。

这种情形的办理办法有两种:

一种比较大略,如果查询存储系统的数据没有找到,则直接设置一个默认值(可以是空值,也可以是详细的值) 存到缓存中,这样第二次读取缓存时就会获取到默认值,而不会连续访问存储系统。

一种须要引入布隆过滤器,它的事理也很大略便是利用高效的数据构造和算法,快速判断出查询的Key是否在数据库中存在,不存在直接返回空,存在就去查了DB,刷新KV再返回值。

缓存击穿

缓存击穿和缓存穿透也有点难以区分,缓存穿透表示的是缓存和数据库中都没有数据,缓存击穿表示缓存中没有数据而数据库中有数据。
缓存击穿是某个热点的key失落效,大并发集中对其进行要求,就会造成大量要求读缓存没读到数据,从而导致高并发访问数据库,引起数据库压力剧增。
这种征象就叫做缓存击穿。

缓存击穿示意图:

缓存击穿

关键在于某个热点的key失落效了,导致大并发集中打在数据库上。
以是要从两个方面办理,第一是否可以考虑热点key不设置过期韶光,第二是否可以考虑降落打在数据库上的要求数量。

紧张有两个办理办法:

利用互斥锁担保同一时候只有一个客户端可以查询底层数据库的这个数据,一旦查到数据就缓存至Redis内,避免其他大量要求同时穿过Redis访问底层数据库。
这种办法会壅塞其他的线程,此时系统的吞吐量会低落热点数据缓存永久不过期。

永不过期有两种办法:

物理不过期,针对热点key不设置过期韶光逻辑过期,把过期韶光存在key对应的value里,如果创造要过期了,通过一个后台的异步线程进行缓存的构建缓存雪崩

缓存雪崩,指的是是缓存不可用,或者同一时候是大量热点key失落效。

两种情形导致的同样的后果便是大量的要求直接落在数据库上,对付一个高并发的业务系统来说,几百毫秒内可能会接到几百上千个要求,最严重的后果便是直接导致数据库宕机,可能会引起连锁反应,导致系统崩溃。

缓存雪崩

缓存雪崩的办理方案可以分为三个维度:

事前:

① 均匀过期:设置不同的过期韶光,让缓存失落效的韶光只管即便均匀,避免相同的过期韶光导致缓存雪崩,造成大量数据库的访问。

② 分级缓存:第一级缓存失落效的根本上,访问二级缓存,每一级缓存的失落效韶光都不同。

③ 热点数据缓存永久不过期。

④ 担保Redis缓存的高可用,防止Redis宕机导致缓存雪崩的问题。
可以利用 Redis集群等办法来避免 Redis 通盘崩溃的情形。

事中:

① 互斥锁:在缓存失落效后,通过互斥锁或者行列步队来掌握读数据写缓存的线程数量,比如某个key只许可一个线程查询数据和写缓存,其他线程等待。
这种办法会壅塞其他的线程,此时系统的吞吐量会低落

② 利用熔断机制,限流降级。
当流量达到一定的阈值,直接返回“系统拥挤”之类的提示,防止过多的要求打在数据库年夜将数据库击垮,至少能担保一部分用户是可以正常利用,其他用户多刷新几次也能得到结果。

事后:

① 开启Redis持久化机制,尽快规复缓存数据,一旦重启,就能从磁盘上自动加载数据规复内存中的数据。

存储层

不管是为了知足业务发展的须要,还是为了提升自己的竞争力,关系数据库厂商(Oracle、DB2、MySQL 等)在优化和提升单个数据库做事器的性能方面也做了非常多的技能优化和改进。
但业务发展速率和数据增长速率,远远超出数据库厂商的优化速率,尤其是互联网业务兴起之后,海量用户加上海量数据的特点,单个数据库做事器已经难以知足业务须要,必须考虑数据库集群的办法来提升性能。

读写分离

读写分离的基本事理是将数据库读写操作分散到不同的节点上,下面是其基本架构图:

读写分离

读写分离的基本实现是:

数据库做事器搭建主从集群,一主一从、一主多从都可以。
数据库主机卖力读写操作,从机只卖力读操作。
数据库主机通过复制将数据同步到从机,每台数据库做事器都存储了所有的业务数据。
业务做事器将写操作发给数据库主机,将读操作发给数据库从机。

主从异步复制事理

读写分离的实现逻辑并不繁芜,但有两个细节点将引入设计繁芜度:主从复制延迟和分配机制。

复制延迟

以 MySQL 为例,主从复制延迟可能达到 1 秒,如果有大量数据同步,延迟 1 分钟也是有可能的。

主从复制延迟会带来一个问题:如果业务做事器将数据写入到数据库主理事器后急速 (1 秒 内)进行读取,此时读操作访问的是从机,主机还没有将数据复制过来,到从机读取数据是读不到最新数据的,业务上就可能涌现问题。

比如说将微博的信息同步给审核系统,以是我们在更新完主库之后,会将微博的 ID 写入行列步队,再由行列步队处理机依据 ID 在从库中 获取微博信息再发送给审核系统。
此时如果主从数据库存在延迟,会导致在从库中获取不到微博信息,全体流程会涌现非常。

复制延迟

办理主从复制延迟的常见方法:

数据的冗余

我们可以在发送行列步队时不仅仅发送微博 ID,而是发送行列步队处理机须要的所有微博信息,借此避免从数据库中重新查询数据。

利用缓存

我们可以在同步写数据库的同时,也把微博的数据写入到缓存里面,行列步队处理机在获取微博信息的时候会优先查询缓存,这样也可以担保数据的同等性。

二次读取

我们可以对底层数据库访问的API进行封装,一次读取从库创造不实时之后再读取一次,例如我们通过微博ID没有在从库里读到微博,那么第二次就直接去主库读取。

查询主库

我们可以把关键业务,或者对实时性有哀求的业务读写操作全部指向主机,非关键业务或者实时性哀求不高的业务采取读写分离。

分配机制

将读写操作区分开来,然后访问不同的数据库做事器,一样平常有两种办法:程序代码封装和中间件封装。

程序代码封装

程序代码封装指在代码中抽象一个数据访问层(以是有的文章也称这种办法为 "中间层封装" ) ,实现读写操作分离和数据库做事器连接的管理。
例如,基于 Hibernate 进行大略封装,就可以实现读写分离,基本架构是:

代码封装读写分离分配

程序代码封装的办法具备几个特点:

实现大略,而且可以根据业务做较多定制化的功能。
每个编程措辞都须要自己实现一次,无法通用,如果一个业务包含多个编程措辞写的多个子系统,则重复开拓的事情量比较大。
故障情形下,如果主从发生切换,则可能须要所有系统都修正配置并重启。

如果不想自己造轮子,也可以用开源的方案,淘宝的TDDL是比较出名的一个。

TDDL

中间件封装

中间件封装指的是独立一套系统出来,实现读写操作分离和数据库做事器连接的管理。
中间件对业务做事器供应 SQL 兼容的协议,业务做事器无须自己进行读写分离。
对付业务做事器来说,访问中间件和访问数据库没有差异,事实上在业务做事器看来,中间件便是一个数据库做事器。

其基本架构是:

数据库中间件

数据库中间件的办法具备的特点是:

能够支持多种编程措辞,由于数据库中间件对业务做事器供应的是标准 SQL 接口。
数据库中间件要支持完全的 SQL 语法和数据库做事器的协议(例如,MySQL 客户端和做事器的连接协议) ,实现比较繁芜,细节特殊多,很随意马虎涌现 bug, 须要较长的韶光才能稳定。
数据库中间件自己不实行真正的读写操作,但所有的数据库操作要求都要经由中间件,中间件的性能哀求也很高。
数据库主从切换对业务做事器无感知,数据库中间件可以探测数据库做事器的主从状态。
例如,向某个测试表写入一条数据,成功的便是主机,失落败的便是从机。

目前开源的数据库中间件有基于 MySQL Proxy 开拓的奇虎 360 的 Atlas 、阿 里 的Cobar、基于 Cobar 开拓的 Mycat 等。

分库分表

读写分离分散了数据库读写操作的压力,但没有分散存储压力,当数据量达到干万乃至上亿条的时候,单台数据库做事器的存储能力会成为系统的瓶颈,紧张表示在这几个方面:

数据量太大,读写的性能会低落,纵然有索引,索引也会变得很大,性能同样会低落。
数据文件会变得很大,数据库备份和规复须要耗费很永劫光。
数据文件越大,极度情形下丟失落数据的风险越高(例如,机房失火导致数据库主备机都发生故障)。

基于上述缘故原由,单个数据库做事器存储的数据量不能太大,须要掌握在一定的范围内。
为了知足业务数据存储的需求,就须要将存储分散到多台数据库做事器上。

业务分库

业务分库指的是按照业务模块将数据分散到不同的数据库做事器。
例如,一个大略的电商网站,包括用户、商品、订单三个业务模块,我们可以将用户数据、商品数据、订单数据分开放到三台不同的数据库做事器上,而不是将所有数据都放在一台数据库做事器上。

业务分库

虽然业务分库能够分散存储和访问压力,但同时也带来了新的问题,接下来我们详细剖析一下。

join 操作问题

业务分库后,原来在同一个数据库中的表分散到不同数据库中,导致无法利用 SQL 的 join 查 询。

例如:"查询购买了扮装品的用户中女性用户的列表〃 这个功能,虽然订单数据中有用户的 ID信息,但是用户的性别数据在用户数据库中,如果在同一个库中,大略的 join 查询就能完成;但现在数据分散在两个不同的数据库中,无法做 join 查询,只能采纳先从订单数据库中查询购买了扮装品的用户 ID 列表,然后再到用户数据库中查询这批用户 ID 中的女性用户列表,这样实现就比大略的 join 查询要繁芜一些。

事务问题

原来在同一个数据库中不同的表可以在同一个事务中修正,业务分库后,表分散到不同的数据库中,无法通过事务统一修正。
虽然数据库厂商供应了一些分布式事务的办理方案(例如,MySQL 的 XA) , 但性能实在太低,与高性能存储的目标是相违背的。

例如,用户下订单的时候须要扣商品库存,如果订单数据和商品数据在同一个数据库中,我们可订单,如果由于订单数据库非常导致天生订单失落败,业务程序又须要将商品库存加上;而如果由于业务程序自己非常导致天生订单失落败,则商品库存就无法规复了,须要人工通过曰志等办法来手工修复库存非常。

本钱问题

业务分库同时也带来了本钱的代价,本来 1 台做事器搞定的事情,现在要 3 台,如果考虑备份,那便是 2 台变成了 6 台。

基于上述缘故原由,对付小公司初创业务,并不建议一开始就这样拆分,紧张有几个缘故原由:初创业务存在很大的不愿定性,业务不一定能发展起来,业务开始的时候并没有真正的存储和访问压力,业务分库并不能为业务带来代价。
业务分库后,表之间的 join 查询、数据库事务无法大略实现了。

业务分库后,由于不同的数据要读写不同的数据库,代码中须要增加根据数据类型映射到不同数据库的逻辑,增加了事情量。
而业务初创期间最主要的是快速实现、快速验证,业务分库会拖慢业务节奏。

单表拆分

将不同业务数据分散存储到不同的数据库做事器,能够支撑百万乃至千万用户规模的业务,但如果业务连续发展,同一业务的单表数据也会达到单台数据库做事器的处理瓶颈。
例如,淘宝的几亿用户数据,如果全部存放在一台数据库做事器的一张表中,肯定是无法知足性能哀求的,此时就须要对单表数据进行拆分。

单表数据拆分有两种办法:垂直分表和水平分表。
示意图如下:

image-20210509213156925

分表能够有效地分散存储压力和带来性能提升,但和分库一样,也会引入各种繁芜性。

两种分表办法可以用一个例子比喻,我们很多人可能都看过这么一篇文章,怎么把苹果切出星星来,答案是横着切。

苹果切分

垂直分表

垂直分表适宜将表中某些不常用且占了大量空间的列拆分出去。
例如,前面示意图中的nickname 和 desc 字段,假设我们是一个婚恋网站,用户在筛选其他用户的时候,紧张是用 age 和 sex 两个字段进行查询,而 nickname 和 description 两个字段紧张用于展示,一样平常不会在业务查询中用到。
description 本身又比较长,因此我们可以将这两个字段独立到其余—张表中,这样在查询 age 和 sex 时,就能带来一定的性能提升。
垂直分表引入的繁芜性紧张表示在表操作的数量要增加。
例如,原来只要一次查询就可以获取name、age、sex、nickname、description, 现在须要两次查询,—次查询获取 name、age、 sex, 另一次查询获取 nickname、desc。

不过比较接下来要讲的水平分表,这个繁芜性便是小巫见大巫了。

水平分表

水平分表适宜表行数特殊大的表,有的公司哀求单表行数超过 5000 万就必须进行分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能。
对付一些比较繁芜的表,可能超过 1000 万就要分表了;而对付一些大略的表,纵然存储数据超过 1 亿行,也可以不分表。
但不管若何,当看到表的数据量达到干万级别时,这很可能是架构的性能瓶颈或者隐患。

水平分表比较垂直分表,会引入更多的繁芜性,紧张表现不才面几个方面:

路由

水平分表后,某条数据详细属于哪个切分后的子表,须要增加路由算法进行打算,这个算法会引入一定的繁芜性。

常见的路由算法有:

范围路由Hash路由

「范围路由」:选取有序的数据列 (例如,整形、韶光戳等) 作为路由的条件,不同分段分散到不同的数据库表中。
以订单 Id 为例,路由算法可以按照 1000万 的范围大小进行分段。
范围路由设计的繁芜点紧张表示在分段大小的选取上,分段太小会导致切分后子表数量过多,增加掩护繁芜度;分段太大可能会导致单表依然存在性能问题,一样平常建议分段大小在 100 万至2000 万之间,详细须要根据业务选取得当的分段大小。

范围路由的优点是可以随着数据的增加平滑地扩充新的表。
例如,现在的用户是 100 万,如果增加到 1000 万,只须要增加新的表就可以了,原有的数据不须要动。
范围路由的一个比较隐含的缺陷是分布不屈均,如果按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1000 条,而其余一个分段实际存储的数据量有 900 万条。

「Hash 路由」:选取某个列 (或者某几个列组合也可以) 的值进行 Hash 运算,然后根据 Hash 结果分散到不同的数据库表中。
同样以订单 id 为例,如果我们一开始就方案了 4个数据库表,路由算法可以大略地用 id % 4 的值来表示数据所属的数据库表编号,id 为 12的订单放到编号为 50的子表中,id为 13的订单放到编号为 61的字表中。

Hash 路由设计的繁芜点紧张表示在初始表数量的选取上,表数量太多掩护比较麻烦,表数量太少又可能导致单表性能存在问题。
而用了 Hash 路由后,增加字表数量是非常麻烦的,所有数据都要重分布。

Hash 路由的优缺陷和范围路由基本相反,Hash 路由的优点是表分布比较均匀,缺陷是扩充新的表很麻烦,所有数据都要重分布。

「配置路由」:配置路由便是路由表,用一张独立的表来记录路由信息。

同样以订单id 为例,我们新增一张 order_router 表,这个表包含 orderjd 和 tablejd 两列 , 根据 orderjd 就可以查询对应的 table_id。

配置路由设计大略,利用起来非常灵巧,尤其是在扩充表的时候,只须要迁移指定的数据,然后修正途由表就可以了。

配置路由的缺陷便是必须多查询一次,会影响整体性能;而且路由表本身如果太大(例如,几亿条数据) ,性能同样可能成为瓶颈,如果我们再次将路由表分库分表,则又面临一个去世循环式的路由算法选择问题。

join 操作

水平分表后,数据分散在多个表中,如果须要与其他表进行 join 查询,须要在业务代码或者数据库中间件中进行多次 join 查询,然后将结果合并。

count()操作

分表后就没那么大略了。
常见的处理办法有下面两种:

count() 相加:详细做法是在业务代码或者数据库中间件中对每个表进行 count操作,然后将结果相加。
这种办法实现大略,缺陷便是性能比较低。
例如,水平分表后切分为 20 张表,则要进行 2 0 次 count()操作,如果串行的话,可能须要几秒钟才能得到结果。

记录数表:详细做法是新建一张表,如果表名为 "记录数表” ,包含 table_name、 row_count两个字段,每次插入或者删除子表数据成功后,都更新 "记录数表“。
这种办法获取表记录数的性能要大大优于 count()相加的办法,由于只须要一次大略查询就可以获取数据。
缺陷是繁芜度增加不少,对子表的操作要同步操作 "记录数表" ,如果有一个业务逻辑遗漏了,数据就会不一致;且针对 "记录数表" 的操作和针对子表的操作无法放在同一事务中进行处理,非常的情形下会涌现操作子表成功了而操作记录数表失落败,同样会导致数据不一致。

此外,记录数表的办法也增加了数据库的写压力,由于每次针对子表的 insert 和 delete 操作都要 update 记录数表,以是对付一些不哀求记录数实时保持精确的业务,也可以通过后台定时更新记录数表。
定时更新实际上便是 "count()相加" 和 "记录数表" 的结合,即定时通过count()相加打算表的记录数,然后更新记录数表中的数据。

order by 操作

水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。

「实现方法」

和数据库读写分离类似,分库分表详细的实现办法也是 "程序代码封装" 和 "中间件封装" ,但实现会更繁芜。
读写分离实现时只要识别 SQL 操作是读操作还是写操作,通过大略的判断SELECT、UPDATE、 INSERT、DELETE 几个关键字就可以做到,而分库分表的实现除了要判断操作类型外,还要判断 SQL 中详细须要操作的表、操作函数(例如 count 函数)、order by、group by 操作等,然后再根据不同的操作进行不同的处理。
例如 order by 操作,须要先从多个库查询到各个库的数据,然后再重新 order by 才能得到终极的结果。

数据异构

完身分库分表往后,我们看到存在一些问题,除了"程序代码封装" 和 "中间件封装"之外,我们还有一种办法,便是数据异构。
数据异构便是将数据进行异地存储,比如业务年夜将MySQL的数据,写一份到Redis中,这便是实现了数据在集群中的异地存储,也便是数据异构。

在数据量和访问量双高时利用数据异构是非常有效的,但增加了架构的繁芜度。
异构时可以通过双写、订阅 MQ 或者 binlog 并解析实现。

双写:在写入数据的时候,同时将数据写入MySQL和异构存储系统;MQ:写入MySQL成功后,发一个mq,缓存读取mq并将写入异构存储系统;binlog:写入MySQL后,缓存系统x消费binlog,将变动写入异构存储系统。

这是一个异构的数据架构示意图:

异构数据架构

在图中用到了ES搜索集群来处理搜索业务,同样也可以我们前面提到的跨库join的问题。

在设计异构的时候,我们可以充分利用一些盛行的NoSQL数据库。
NoSQL只管已经被证明不能取代关系型数据库,但是在很多场景下是关系型数据库的有力补充。

举几个例子,像我们熟习的Redis这样的KV存储,有极高的读写性能,在读写性能有哀求的场景可以利用;

Hbase、Cassandra 这样的列式存储数据库。
这种数据库的特点是数据不像传统数据库以行为单位来存储,而因此列来存储,适用于一些离线数据统计的场景;

MongoDB、CouchDB 这样的文档型数据库,具备 Schema Free(模式自由)的特点,数据表中的字段可以任意扩展,可以用于数据字段不固定的场景。

查询维度异构

比如对付订单库,当对其分库分表后,如果想按照商家维度或者按照用户维度进行查询,那么是非常困难的,因此可以通过异构数据库来办理这个问题。
可以采取下图的架构。

异构1

或者采取下图的ES异构:

异构2

异构数据紧张存储数据之间的关系,然后通过查询源库查询实际数据。
不过,有时可以通过数据冗余存储来减少源库查询量或者提升查询性能。

聚合据异构

商品详情页中一样平常包括商品基本信息、商品属性、商品图片,在前端展示商品详情页时,是按照商品 「ID」 维度进行查询,并且须要查询 3 个乃至更多的库才能查到所有展示数据。
此时,如果个中一个库不稳定,就会导致商品详情页涌现问题,因此,我们把数据聚合后异构存储到 「KV」 存储集群(如存储 「JSON」 ), 这样只须要一次查询就能得到所有的展示数据。
这种办法也须要系统有了一定的数据量和访问量时再考虑。

聚合据异构

高并发架构要点

通过前面的内容,已经差不多理解高并发的架构是一个什么样,接下来做一些总结和补充。

高性能要点

高性能要点

高可用要点

高可用要点

除了从技能的角度来考虑,担保高可用同样须要良好的组织制度,来担保做事涌现问题的快速规复。

高扩展要点

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

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

3、业务层的拆分:最常见的是按照业务维度拆(比如电阛阓景的商品做事、订单做事等),也可以按照核心要乞降非核心要求拆分,还可以按照要求源拆(比如To C和To B,APP和H5 )。

总结了很多有关于java口试的资料,希望能够帮助正在学习java的小伙伴。
由于资料过多不便揭橥文章,创作不易,望小伙伴们能够给我一些动力连续创建更好的java类学习资料文章,

请多多支持和关注小作,别忘了点赞+评论+转发。
右上角私信我回答【03】即可领取免费学习资料感激啦!

原文出处:https://mp.weixin.qq.com/s/a7dx87_ipolmzxgCxxRcrA