本文目录以下:前端
一、概述 一、1 系统性能定义 一、2 目的意义 二、性能优化技术 二、1 前端优化 二、2 后端优化 三、总结
一、概述nginx
最近看了不少关于系统性能调优的文章,发现不少文章都是介绍某一方面的,例如专门数据库方面的优化、前端页面的优化等等都不是很全面,这里结合我在工做中的一些实践对系统性能调优技术来一个综合性的分享。git
一、1 系统性能定义github
如上图,性能就是吞吐量加延迟,这两个相互矛盾又相互协调构成了一个系统性能的定义:算法
- Throughput ,吞吐量。也就是每秒钟能够处理的请求数,任务数。
- Latency, 系统延迟。也就是系统在处理一个请求或一个任务时的延迟。
通常来讲,一个系统的性能受到这两个条件的约束,缺一不可。好比,个人系统能够顶得住一百万的并发,可是系统的延迟是2分钟以上,那么,这个一百万的负载毫无心义。系统延迟很短,可是吞吐量很低,一样没有意义。因此,一个好的系统的性能测试必然受到这两个条件的同时做用。 有经验的朋友必定知道,这两个东西的一些关系:sql
- Throughput越大,Latency会越差。由于请求量过大,系统太繁忙,因此响应速度天然会低。
- Latency越好,能支持的Throughput就会越高。由于Latency短说明处理速度快,因而就能够处理更多的请求。
一、2 目的意义数据库
本文的目的是经过讲解系统性能让你们在后续的工做中可以带着产品化的思路去优化本身的代码包括先后台、数据库等,自测过程当中咱们能够利用压力性能测试pylot、Fiddler、单元测试等工具去发现系统的问题从而去优化提升系统的质量,这样经过团队的配合和努力来提升加强用户的体验从而提升咱们公司的竞争力!编程
二、性能优化技术
如下性能优化技术须要咱们在本身工做过程当中不断积累和总结,在工做中配合一些专业的测试工具去发现性能的瓶颈,这里把性能优化技术分为两块分别是前端和后端的优化。
二、1 前端优化
2.1.1 负载均衡
经过DNS的负载均衡器(通常在路由器上根据路由的负载重定向)能够把用户的访问均匀地分散在多个Web服务器上。这样能够减小Web服务器的请求负载。由于http的请求都是短做业,因此,能够经过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户链接与其最近的服务器(CDN一般伴随着分布式存储)。
CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽量避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。
CDN的通俗理解就是网站加速,能够解决跨运营商,跨地区,服务器负载能力太低,带宽过少等带来的网站打开速度慢等问题。
CDN的特色和优点:
一、本地Cache加速 提升了企业站点(尤为含有大量图片和静态页面站点)的访问速度,并大大提升以上性质站点的稳定性
二、镜像服务 消除了不一样运营商之间互联的瓶颈形成的影响,实现了跨运营商的网络加速,保证不一样网络中的用户都能获得良好的访问质量。
三、远程加速 远程访问用户根据DNS负载均衡技术智能自动选择Cache服务器,选择最快的Cache服务器,加快远程访问的速度
四、带宽优化 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减小远程访问的带宽、分担网络流量、减轻原站点WEB服务器负载等功能。
2.1.2 减小请求和2.1.3缩减网页
减小请求数
(1)系统某个页面的加载每每伴随着多个请求的发生,请求越多吞吐量越大,延迟就会变大,这里就要考虑优化请求数了,咱们可使用Fiddler等工具查看某个网页的请求数,以下图,若是咱们的一个网页引用了不少样式和js,例如一个页面引用了10个css和10个js,那么咱们应该考虑把某些样式和js合并起来;
(2)Css Sprites:有不少图片咱们其实能够用一张图片来代替的,通常须要跟美工或UI设计器配合一块儿来作的,美工或UI设计师去设计出来以后告诉咱们图片中具体元素的位置或者封装在css中,研发这边直接调用便可。
异步
系统某个页面中若是有一个请求的响应超过0.5秒以上或者请求的响应量大于300KB的话咱们应该考虑进行异步请求,还有就是一些服务的调用这些尽可能不要用同步,一阻塞整个网站的体验会很是差;
CSS/JS压缩
能够借助一些开源的压缩工具,像开源的yuicompressor,发布或发包时把js和css都压缩一下,这样js和css文件就会很是小了;
GZIP压缩
使用GZIP压缩能够下降服务器发送的字节数,能让客户感受到网页的速度更 快也减小了对带宽的使用状况;
IIS里面也能够设置GZIP压缩,能够压缩应用程序文件和静态文件,具体百度。
精简代码
最高效的程序就是不执行任何代码的程序,因此,代码越少性能就越高。关于代码级优化的技术大学里的教科书有不少示例了。如:减小循环的层数,减小递归,在循环中少声明变量,少作分配和释放内存的操做,尽可能把循环体内的表达式抽到循环外,条件表达的中的多个条件判断的次序,尽可能在程序启动时把一些东西准备好,注意函数调用的开销(栈上开销),注意面向对象语言中临时对象的开销,当心使用异常。
开源框架
如今开源的好东西太多了,关键是你要有一双慧眼,向你们推荐开源中国社区、github、codeplex,我发现如今比较厉害的开发者就是一个很牛逼的模仿者,消化掉成为本身的其实就是一种创新;
2.1.4 优化查询
(1)SQL语句的优化
关于SQL语句的优化,首先也是要使用工具,好比:MySQL SQL Query Analyzer,Oracle SQL Performance Analyzer,或是微软SQL Query Analyzer,基本上来讲,全部的RMDB都会有这样的工具,来让你查看你的应用中的SQL的性能问题。 还可使用explain来看看SQL语句最终Execution Plan会是什么样的。
还有一点很重要,数据库的各类操做须要大量的内存,因此服务器的内存要够,优其应对那些多表查询的SQL语句,那是至关的耗内存。
下面我根据我有限的数据库SQL的知识说几个会有性能问题的SQL:
全表检索。好比:select * from user where lastname = “xxxx”,这样的SQL语句基本上是全表查找,线性复杂度O(n),记录数越多,性能也越差(如:100条记录的查找要50ms,一百万条记录须要5分钟)。对于这种状况,咱们能够有两种方法提升性能:一种方法是分表,把记录数降下来,另外一种方法是建索引(为lastname建索引)。索引就像是key-value的数据结构同样,key就是where后面的字段,value就是物理行号,对索引的搜索复杂度是基本上是O(log(n)) ——用B-Tree实现索引(如:100条记录的查找要50ms,一百万条记录须要100ms)。
索引。对于索引字段,最好不要在字段上作计算、类型转换、函数、空值判断、字段链接操做,这些操做都会破坏索引本来的性能。固然,索引通常都出如今Where或是Order by字句中,因此对Where和Order by子句中的子段最好不要进行计算操做,或是加上什么NOT之类的,或是使用什么函数。
多表查询。关系型数据库最多的操做就是多表查询,多表查询主要有三个关键字,EXISTS,IN和JOIN(关于各类join,能够参看图解SQL的Join一文)。基原本说,现代的数据引擎对SQL语句优化得都挺好的,JOIN和IN/EXISTS在结果上有些不一样,但性能基本上都差很少。有人说,EXISTS的性能要好于IN,IN的性能要好于JOIN,我各人以为,这个还要看你的数据、schema和SQL语句的复杂度,对于通常的简单的状况来讲,都差很少,因此千万不要使用过多的嵌套,千万不要让你的SQL太复杂,宁肯使用几个简单的SQL也不要使用一个巨大无比的嵌套N级的SQL。还有人说,若是两个表的数据量差很少,Exists的性能可能会高于In,In可能会高于Join,若是这两个表一大一小,那么子查询中,Exists用大表,In则用小表。这个,我没有验证过,放在这里让你们讨论吧。另,有一篇关于SQL Server的文章你们能够看看《IN vs JOIN vs EXISTS》。
JOIN操做。有人说,Join表的顺序会影响性能,只要Join的结果集是同样,性能和join的次序无关。由于后台的数据库引擎会帮咱们优化的。Join有三种实现算法,嵌套循环,排序归并,和Hash式的Join。(MySQL只支持第一种)。
- 嵌套循环,就好像是咱们常见的多重嵌套循环。注意,前面的索引说过,数据库的索引查找算法用的是B-Tree,这是O(log(n))的算法,因此,整个算法复法度应该是O(log(n)) * O(log(m)) 这样的。
- Hash式的Join,主要解决嵌套循环的O(log(n))的复杂,使用一个临时的hash表来标记。
- 排序归并,意思是两个表按照查询字段排好序,而后再合并。固然,索引字段通常是排好序的。
仍是那句话,具体要看什么样的数据,什么样的SQL语句,你才知道用哪一种方法是最好的。
部分结果集。咱们知道MySQL里的Limit关键字,Oracle里的rownum,SQL Server里的Top都是在限制前几条的返回结果。这给了咱们数据库引擎不少能够调优的空间。通常来讲,返回top n的记录数据须要咱们使用order by,注意在这里咱们须要为order by的字段创建索引。有了被建索引的order by后,会让咱们的select语句的性能不会被记录数的所影响。使用这个技术,通常来讲咱们前台会以分页方式来显现数据,Mysql用的是OFFSET,SQL Server用的是FETCH NEXT,这种Fetch的方式其实并很差是线性复杂度,因此,若是咱们可以知道order by字段的第二页的起始值,咱们就能够在where语句里直接使用>=的表达式来select,这种技术叫seek,而不是fetch,seek的性能比fetch要高不少。
字符串。正如我前面所说的,字符串操做对性能上有很是大的恶梦,因此,能用数据的状况就用数字,好比:时间,工号,等。
全文检索。千万不要用Like之类的东西来作全文检索,若是要玩全文检索,能够尝试使用Sphinx。
其它。
- 不要select *,而是明确指出各个字段,若是有多个表,必定要在字段名前加上表名,不要让引擎去算。
- 不要用Having,由于其要遍历全部的记录。性能差得不能再差。
- 尽量地使用UNION ALL 取代 UNION。
- 索引过多,insert和delete就会越慢。而update若是update多数索引,也会慢,可是若是只update一个,则只会影响一个索引表。
(2)DBCC DBREINDEX重建索引
优化实战
2.1.5 静态化
静态化一些不常变的页面和数据,并gzip一下。使用nginx的sendfile功能可让这些静态文件直接在内核心态交换,能够极大增长性能。
通常咱们能够作一个静态文件管理功能,能够把咱们网站的一些栏目直接经过请求/响应的方式在服务器上直接生成静态文件,固然这里能够设置一个时间频率,用户直接访问静态页面访问效率确定很是高!
2.1.6 缓存
一般,应用程序能够将那些频繁访问的数据,以及那些须要大量处理时间来建立的数据存储在内存中,从而提升性能;它包括应用程序缓存和页输出缓存;
通常咱们大部分用的是应用程序缓存
缓存的应用场景主要有:
OutputCache
咱们能够用Fiddler找出一些内容几乎不会改变的页面,给它们设置OutputCache指令便可;
对于设置过OutputCache的页面来讲,浏览器在收到这类页面的响应后,会将页面响应内容缓存起来。 只要在指定的缓存时间以内,且用户没有强制刷新的操做,那么就根本不会再次请求服务端, 而对于来自其它的浏览器发起的请求,若是缓存页已生成,那么就能够直接从缓存中响应请求,加快响应速度。 所以,OutputCache指令对于性能优化来讲,是颇有意义的(除非全部页面页面都在频繁更新)。
应用程序缓存
应用程序缓存提供了一种编程方式,可经过键/值对将任意数据存储在内存中,这里提供一个asp.net对缓存有效封装的例子,见缓存机制理解及C#开发使用。
缓存能够用来缓存动态页面,也能够用来缓存查询的数据。缓存一般有那么几个问题:
1)缓存的更新。也叫缓存和数据库的同步。有这么几种方法,一是缓存time out,让缓存失效,重查,二是,由后端通知更新,一量后端发生变化,通知前端更新。前者实现起来比较简单,但实时性不高,后者实现起来比较复杂 ,但实时性高。
2)缓存的换页。内存可能不够,因此,须要把一些不活跃的数据换出内存,这个和操做系统的内存换页和交换内存很类似。FIFO、LRU、LFU都是比较经典的换页算法。
3)缓存的重建和持久化。缓存在内存,系统总要维护,因此,缓存就会丢失,若是缓存没了,就须要重建,若是数据量很大,缓存重建的过程会很慢,这会影响生产环境,因此,缓存的持久化也是须要考虑的。
【编辑推荐】