聊聊性能优化模式

我我的有收藏感兴趣的技术连接的习惯,最近太忙,没太多时间看收藏的技术贴,可贵今天有空,看了篇美团技术团队的关于性能优化的内容,html

感受不错,将其中的一些观点和方法作了总结概括,其中还掺杂一些我的的思考,写下这篇博客,以备往后查阅。。。java

原文连接:性能优化模式数据库

 

、性能优化的三个方面编程

一、下降响应时间设计模式

二、提升系统吞吐量缓存

三、提升服务的可用性安全

三者的关系:在某些场景下互相矛盾,不可兼得性能优化

 

2、性能优化面临的挑战restful

一、日益增加的用户数量网络

二、愈来愈复杂的业务

三、急剧膨胀的数据

 

3、性能优化的目标

在保持和下降系统99%RT的前提下,不断提升系统吞吐量,提升流量高峰时期的服务可用性。

 

4、本文涉及的几种原则

一、最小可用原则

也称为快速接入原则,有两个关注点:

①、快速接入,快速完成;

②、实现核心功能可用;

目标:经过下降开发测试周期,增长试错机会,快速接入而实现风险可控;

注意:这并不意味着成本的下降,而须要为重构作好准备;

相关阅读:在产品设计上有一个名词叫作MVP,即最小可行性产品,这一观点在《人人都是产品经理1.0记念版》一书中有相关章节描述。

二、经济原则

软件项目生命周期包括:预研、设计、开发、测试、运行、维护等阶段。

上面提到的最小可用原则主要运用在预研阶段,而经济原则既能够用在整个软件生命周期里,或只关注某一个或者几个阶段;

例如:运行时经济原则须要考虑的系统成本包括单次请求的CPU、内存、网络、磁盘消耗等;

设计阶段的经济原则要求避免过分设计;开发阶段的经济原则可能关注代码复用,工程师资源复用等。

三、代码复用原则

该原则分两个层次:

①、使用已有的解决方案或调用已存在的共享库(Shared Library),也称为方案复用

②、直接在现有的代码库中开发,也称之为共用代码库

方案复用原则出发点就是最大限度地利用手头已有的解决方案,即便这个方案并很差;方案的形式能够是共享库,也能够是已存在的服务。

优势:提升生产效率;

相似:方案复用相似于微服务架构(Microservice Architecture),restful风格,设计出可重用性的组件或服务,经过分层组织各层组件来实现良好的架构。

共用代码库原则要求代码库中的全部功能编译时可见,新功能代码能够无边界的调用老代码;另外,原代码库已存在的各类运行、编译、测试、配置环境可复用。

优势:

①、充分利用代码库中已有的基础设施,快速接入新业务;

②、直接调用原代码中的基础功能,避免网络或进程间调用开销,性能更佳;

相似:共用代码库和总体架构(Monolithic Architecture)很接近,它但愿尽量在一套代码库中开发,经过直接调用代码中的基础功能实现性能优化和快速迭代。

四、奥卡姆剃刀原则

通常状况下,系统的代码量会随着功能的增长而变多,健壮性有时候也须要经过编写异常处理代码来实现,异常考虑越全面,异常处理的代码量就越大。

随着代码量的增大,引入BUG的几率也越大,系统也就越不健壮。从另外一个方面来讲,异常流程处理代码也要考虑健壮性的问题,这就造成了无限循环。

奥卡姆剃刀原则要求

①、一个功能模块如非必要,就不要;

②、一段代码如非必写,则不写;

区别:最小可用原则主要运用于产品MVP阶段,奥卡姆剃刀原则主要指系统设计和代码编写两个方面,这是彻底不一样的两个概念;

MVP包含系统设计和代码编写,但同时,系统设计和代码编写也能够发生在成熟系统的迭代阶段。

 

、性能恶化模式

性能优化的目标之一就是避免系统进入性能恶化模式;

不一样性能优化模式多是避免同一种性能恶化模式;

同一种性能优化模式可能在不一样阶段避免不一样的性能恶化模式;

常见的性能恶化模式

1、长请求拥塞恶化模式

介绍:单次请求时延变长致使系统性能恶化甚至崩溃的模式;

典型场景:复杂的业务场景依赖于多个服务,其中某个服务的响应时间变长,随之系统总体响应时间变长,进而出现CPU、内存、Swap报警;

具体表现:被依赖服务可用性下降、大量RT变长致使线程堆积、内存使用增长,频繁GC,RT时间变得更长,最终致使服务完全崩溃;

表现方式

线程数变多致使县城之间CPU资源竞争,反过来进一步延长了单次请求时间;

线程数增多及线程中缓存变大,内存消耗加重,java语言的服务会频繁的full GC(垃圾回收),致使单次请求时间变得更长;

内存使用变多,使操做系统内存不足,必须使用Swap(内存)交换,可能致使服务崩溃;

恶化流程图以下

2、反复缓存恶化模式

介绍:为下降响应时间,加缓存是种颇有效的方式,缓存数据越多,命中率越高,ART就越快;

典型场景:流量高峰冲击时,系统内存使用增多,出发了JVM进行full GC,进而致使大量缓存被释放,而大量请求又使得缓存被迅速填满,

反复缓存致使频繁的full GC,频繁的full GC每每致使系统性能急剧恶化;

缘由:反复缓存所致使性能恶化的缘由是无节制的使用缓存;

指导原则:全局考虑,精细规划,确保系统彻底缓存状况下系统仍然不会频繁full GC,严格控制缓存大小,甚至废除缓存;

解决措施:线性的增长机器和提升机器的内存大小,能够显著减小系统崩溃的几率;

恶化流程图以下:

3、屡次请求杠杆恶化模式

介绍:客户端一次点击每每会出发屡次服务端请求,每一个服务端请求触发更多底层服务请求,请求层级越多,杠杆效应越大;

典型场景:屡次请求杠杆模式下运行的分布式系统,深层次的服务需处理大量请求,容易成为系统瓶颈;

另外大量请求会给网络带来巨大压力,可能会成为系统完全崩溃的导火索;

表现方式:性能恶化和流量之间每每遵循指数曲线关系,且线性增长机器解决不了可用性问题;

恶化流程图以下:

 

6、性能优化模式

1、水平分割模式

动机:典型的服务端流程包含四个环节:接受请求、获取数据、处理数据、返回结果,大部分耗时长的服务发生在中间两个环节。

若是服务处理请求采用的是串行调用,那么其累加效应会极大延长单次请求RT,这就增长了系统进入长请求拥塞恶化模式的几率,进一步的下降系统性能。

若是能对不一样的业务请求并行处理,请求总耗时就会大大下降。以下图所示:

Client须要对三个服务进行调用,若是采用顺序调用模式,系统的响应时间为18ms,而采用并行调用只须要7ms。

关于client和service之间的链接机制,具体可参考我以前的博客:http链接管理

水平分割模式原理

将整个请求流程切分为必须相互依赖的多个Stage,每一个Stage包含相互独立的多种业务处理。切分以后,水平分割模式串行处理多个Stage,可是在Stage内部并行处理。

一次请求总耗时等于各个Stage耗时总和,每一个Stage所耗时间等于该Stage内部最长的业务处理时间。

优化关键点:减小Stage数量和下降每一个Stage耗时。

优势:

①、下降系统的平均响应时间和TP95响应时间,以及流量高峰时系统崩溃的几率。须要明白的一点:有时候,即便少许的并行化也能够显著提升总体性能

②、代码重构比较复杂,可是水平切割模式很是容易理解,只要熟悉系统的业务,识别出能够并行处理的流程,就可以进行水平切割;

③、对于新系统而言,若是存在可预见的性能问题,把水平分割模式做为一个重要的设计理念将会大大地提升系统的可用性、下降系统的重构风险;

④、有效、容易识别和理解;

 

二、垂直分割模式

动机:不一样业务功能并存于同一个运行系统里面意味着资源共享,同时也意味着资源使用冲突。

不一样业务功能,不管其调用量多小,都有一些内存开销。对于存在大量缓存的业务功能,数量的增长会极大地提升内存消耗,从而增大系统进入反复缓存反模式的几率。

思路:将系统按照不一样的业务功能进行分割,主要有两种分割模式:部署垂直分割和代码垂直分割

部署垂直分割:按照可用性要求将系统进行等价分类,不一样可用性业务部署在不一样机器上,高可用业务单独部署;

代码垂直分割:让不一样业务系统不共享代码,完全解决系统资源使用冲突问题。

缺点:

①、增长了维护成本。一方面代码库数量增多提升了开发工程师的维护成本,另外一方面,部署集群的变多会增长运维工程师的工做量;

②、代码不共享所致使的重复编码工做。

优势:

①、简单有效,特别适用于系统已经出现问题而又须要快速解决的场景;

②、部署层次的分割既安全又有效(大部分状况下,即便不增长机器,仅经过部署分割,系统总体吞吐量和可用性都有可能提高);

注意点:对于代码层次的分割,开发工程师须要在业务承接效率和系统可用性上面作一些折衷考虑。

 

三、恒变分离模式

原理:基于性能的设计要求变化的数据和不变的数据分开,而在面向对象设计中,为了便于对一个对象有总体的把握,

紧密相关的数据集合每每被组装进一个类,存储在一个数据库表,即便有部分数据冗余。

不少系统的主要工做是处理变化的数据,若是变化的数据和不变的数据被紧密组装在一块儿,系统对变化数据的操做将引入额外的开销。

而若是易变数据占总数据比例很是小,这种额外开销将会经过杠杆效应恶化系统性能。

分离易变和恒定不变的数据在对象建立、内存管理、网络传输等方面都有助于性能提升。

缺点

①、不符合面向对象的设计原则;

②、增长了类不变量的维护难度;

③、一张数据库表变成多张,增长维护成本。

恒变分离模式须要知足两个条件

①、易变数据占总体数据比例很低(比例越低,杠杆效应越大);

②、易变数据所致使的操做又是系统的主要操做;

分离原则:

对于复杂的业务系统,尽可能按照面向对象的原则进行设计,只有在性能出现问题的时候才开始考虑恒变分离模式;

而对于高性能,业务简单的基础数据服务,恒变分离模式应该是设计之初的一个重要原则。

 

四、数据局部性模式

动机:数据局部性模式是屡次请求杠杆反模式的针对性解决方案。

典型场景:大数据和强调个性化服务的时代,一个服务消费几十种不一样类型数据的现象很是常见,同时每种类型数据服务均可能须要一个大的集群提供服务。

这就意味着客户端的一次请求有可能会致使服务端成千上万次调用操做,很容易使系统进入屡次请求杠杆反模式。

数据服务数量暴增的缘由:

①、缓存滥用以及缺少规划,

②、数据量太大以致于没法在一台机器上提供全量数据服务,数据局部性模的核心思想是合理组织数据服务,减小服务调用次数。

优化方案:

①、对服务进行从新规划;

②、对客户端进行优化,包括:

  本地缓存,对于一致性要求不高且缓存命中率较高的数据服务,本地缓存能够减小服务端调用次数;

  批处理,对于单机或者由等价的机器集群提供的数据服务,尽量采用批处理方式,将多个请求合成在一个请求中;

  客户端Hash,对须要经过Hash将请求分配到不一样数据服务机器的服务,尽可能在客户端Hash,对于落入同一等价集群的请求采用批处理方式进行调用。

 

五、实时离线分离模式

该模式的极端要求:离线服务永远不要调用实时服务,严格地讲它不是一种系统设计模式,而是一种管理规范。

离线服务和在线服务从可用性、可靠性、一致性的要求上彻底不一样。

原则上,应该遵循的就是离线服务编程规范,按照在线服务编程规范要求,成本就会大大提升,不符合经济原则;

从另一方面讲,按照离线服务的需求去写在线服务代码,可用性、可靠性、一致性等每每得不到知足。

具体而言,实时离线分离模式建议以下几种规范

若是离线程序须要访问在线服务,应该给离线程序单独部署一套服务;

相似于MapReduce的云端多进程离线程序禁止直接访问在线服务;

分布式系统永远不要直接写传统的DBMS。

优缺点:

须要为在线环境和离线环境单独部署,维护多套环境所带来运维成本;

在线环境的数据在离线环境中可能很难获取;;

BUT:听从实时离线分离模式是一个很是重要的安全管理准则,任何违背这个准则的行为都意味着系统性安全漏洞,都会增大线上故障几率

 

六、降级模式

降级模式是系统性能保障的最后一道防线。理论上讲,不存在绝对没有漏洞的系统,或者说,最好的安全措施就是为处于崩溃状态的系统提供预案。

从系统性能优化的角度来说,无论系统设计地多么完善,总会有一些意料以外的状况会致使系统性能恶化,最终可能致使崩溃。

对于要求高可用性的服务,在系统设计之初,就必须作好降级设计。

良好的降级方案应该包含以下措施

①、在设计阶段,肯定系统的开始恶化数值指标(例如:响应时间,内存使用量);

②、当系统开始恶化时,须要第一时间报警;

③、在收到报警后,或者人工手动控制系统进入降级状态,或者编写一个智能程序让系统自动降级;

典型的降级策略有三种:流量降级、效果降级和功能性降级。

流量降级是指当经过主动拒绝处理部分流量的方式让系统正常服务未降级的流量,这会形成部分用户服务不可用;

效果降级表现为服务质量的降级,即在流量高峰时期用相对低质量、低延时的服务来替换高质量、高延时的服务,保障全部用户的服务可用性;

功能性降级也表现为服务质量的降级,指经过减小功能的方式来提升用户的服务可用性。

区别:效果降级强调的是主功能服务质量的降低,功能性降级更多强调的是辅助性功能的缺失。

优缺点:

在肯定使用降级模式的前提下,工程师须要权衡这三种降级策略的利弊;

对于不能接受降级后果的系统,必需要经过其余方式来提升系统的可用性;

总结:降级模式是一种设计安全准则,任何高可用性要求的服务,必需要按照降级模式的准则去设计。

对于处于MVP阶段的系统,或者对于可用性要求不高的系统,降级模式并不是必须采纳的原则。