本文主要是思维性的总结,是总结优化的方法学,对方面上面的错误进行总结。不会涉及到前端具体的技术,比如对js和css进行压缩、合并,减少http请求,缓存头控制等等。这些那本《高性能建站指南》都有现成的。
基于本人在多家公司分别遇到的网站速度与性能问题,多年所积累出的干货;有的开发10年经验,在遇到网站速度问题时,也仍然在犯同样的错误。
一、背景与思维方式
常见的情况:
- 使用的是1m带宽(因为带宽是比较昂贵的资源,刚开始购买会比较少,起初够用了)。基于这个情况,是不是带宽少了呢?加带宽?
- js、css之类的文件也并没有压缩,为了提高高速度,那是不是应该要压缩一下呢?比如合并多个js文件,减少http请求次数(根据那边前端方面优化经典书籍《高性能建站指南》),又或者因为js都比较乱,都是逐个执行;所以为了让浏览器快速渲染出效果来可以考虑把js都改写以下,等到页面渲染完毕再执行
- php磁盘级的缓存也没有做,是不是这个几个页面程序执行的时候,查询数据库太多导致速度呢?也就是说数据库的问题
- php程序还可以优化一下,是不是可以安装什么zendoptiomizer之类的代码加速器呢?或者把代码改写一下?
上面都只是猜测出的可能原因。几种情况都有可能。上面的办法都是可以作为优化的工作内容来做的。比如把js、css文件压缩,确实是可以提高速度,这是一个常识!
还可以把数据库优化一下啊;可以安装什么zend加速器,对代码减少编译环节。也可以把数据库的数据做一些磁盘级的缓存起来,减少操作数据库。
诸如此类的优化措施,都是能够提高速度的。
但这些都存在思维方式错误。在以前呆的好几家公司,遇到速度问题,同事都抱着类似思路来优化的。结果都折腾时间和精力,还是没解决好。
我发现,身边一些同事的思维方式是这样子的:
- 靠猜测,主观判断。当网站的速度慢的时候,很多人靠猜测形式,觉得带宽只有1m,所以先把带宽加大。
我习惯不能靠猜、要以收集的数据为依据(实时记录下来的性能数据),比如做脚本统计每次执行耗时,收集一定量的数据,就知道最耗时间的地方在哪里了,不是看表象认为哪里慢就哪里慢。现在,即便我大体判断性能瓶颈在哪里,我也不会说,我希望用数据来支持
- 多多益善的思维。压缩js和css文件、提高带宽、做磁盘缓存等都能优化速度。去做这些是多多益善,但实际中我发现,由于老板让我们急着要解决网站速度问题,这是当务之急。我们抱着多多益善的思维方式,把上面的都做遍了,速度还是没提高。折腾一段时间,与预期速度提升不符。
- 没有主次矛盾的概念,也就是性能瓶颈。我觉得这是我见过最多的。这点影响了他们把众多的优化方式都去实施,比如压缩js、做缓存、提高带宽。其实定位到性能瓶颈,马上就能解决速度问题。其他慢慢优化。能够满足老板和其他部门的压力。
我把上面的叫做纯技术思路,纯技术思维来看待问题。从技术角度,css和js文件压缩、php程序优化、做缓存、数据库优化,都是没有错误的。从技术层面是往好的方面走。
就像解决12306的网上售票问题的时候,很多仅仅是技术思维来解决。其实某人提出从业务层面调整售票方式,整个性能就会改善,理由是基于:人家现有的窗口售票能够满足日常所需,那本身不是这个系统存在瓶颈。
二、未必与工作年限长有关
从经历来看,我发现,网站速度优化,与多少年开发经验没有直接关联,我以前分别遇到过有10年、20年软件开发经验的同事,在解决速度问题的时候,也是一种纯技术思路。
这是几年前的事了,我亲身经历的一个例子:技术经理是79年的。其实我们的网站访问速度还不错。而老板看到同行的网站网站怎么快些。(人家才多少个技术,我们这么多。应该要达到)。
技术经理把一些方法都尝试了:研究同行的no-cache头,唉,他们的怎么有,而我们的没有。好,我们也要像他们那样做。我们还把代码片段给缓存起来,花了不少时间。可惜,结果速度没有明显提高。有,也只是根据感觉:“哦,我刚才加了缓存控制后,发现服务器的负载低了点。但是怎么又变高了呢。”。“我已经开启了压缩,怎么没明显改善呢?”,其实,加服务器缓存控制,代码判断缓存。肯定是有效果的。但能提高多少?或者说目前这个不算瓶颈。就算你把其他的优化得再好。也没提高整体。没找到瓶颈所在,结果各自方法都试过了。还是没用。白忙活。如果一开始能够分析出瓶颈。着力点完全不同。
那晚技术部经理拉着技术部同事加班,还是没搞定。只能收工。瞎折腾。
另外,我记得当时在那个公司,后台涉及很多统计方面功能,经常一点卡死,慢得已经不能正常使用。瓶颈就在数据库。php程序方面没多少可以优化的,即便改写程序,也不会发生本质改变,因为瓶颈不在这。
因为不是专业的运维人员,所以,我们不知道怎么去分析性能瓶颈。只是根据感觉,这样做可以提高点性能。先这样做吧。
后面还会说到我在c公司的例子,开发经验20年。
其实,我有个习惯,不迷信权威,10年、20年工作经验,只要你说的对,我都认同,但我不会不假思索被所谓10年、20年的经验标签所影响,结果接受错误的东西。比如是否使用存储过程来封装业务逻辑,在传统银行、金融领域做开发的人员倾向于使用(也许某些技术会被10年开发经验变得不假思索),但在互联网这种思路不见得是对的。
关于存储过程封装业务逻辑,请参考我的文章:http://www.cnblogs.com/wangtao_20/p/3475184.html
关于权威、头衔对人思考的影响,请参考我的文章:http://www.cnblogs.com/wangtao_20/p/3652225.html
三、优化观念的改善
1、主次矛盾思维-定位性能瓶颈
在解决网站速度问题经历中,我并不是什么都去优化。
我的办法是,找哪个是瓶颈,也就是主要矛盾。如果主要矛盾没解决。对其他小矛盾无论如何做优化,速度都不会发现明显提高。这种思维方式,也是某次遇到实际问题,同学跟我说瓶颈在数据库,因此我得到启发。在后续的优化工作中,我首先不会去做什么优化,我会第一找到速度的瓶颈在哪里,这种方式在实践中与同事的进行对比,差异明显:既解决了问题,又减少了自己的瞎折腾。
很多人听我说到找瓶颈法,可能会觉得不以为然,或者无动于衷。我的技术同事也是对我提出的方式,没有明显的感触。这可能与我没法以口头形式完整描述有关。
其实,这些都是我实践出来的干货,其中有见过一些同事失败的尝试,最终才形成了一种规律性的东西。由于经历过的几家公司遇到同样的问题,后来发现不仅仅是个例现象,是很多技术同事没有学会正确的方法论,用试一试的心态,偶尔可能试对了,只是偶尔命中。以后遇到复杂点的,还靠试来试去,就没眉目了。
2、瓶颈法,符合"先抗住再优化"的原则
比如侦测定位到数据库是瓶颈所在,那么优先解决数据库问题,这样能够保证抗住.而至于js、css压缩节省带宽等是属于优化部分,后期再做,即便现在做了,速度并不会明显改善.经常看到不少同事做着"多多益善"的无用功,所以我自信我的瓶颈定位法。与现实中的哲学也是非常符合的。现在再次感悟到,做技术的上升到哲学层面来解决问题,是必要的。这里就是主次矛盾法。
这个观点很受用。以前我写过一篇文章,专门对此做了总结:http://www.cnblogs.com/wangtao_20/p/3243009.html
接触的实际项目:电商网站(当时瓶颈在数据库)、外贸网站(当时发现瓶颈在距离远方面,程序优化做的是无用功,可通过带宽来调整)、生活服务网站(发现瓶颈在程序方面)。定位过几次速度问题的瓶颈,找到瓶颈,避免了无效的折腾。
归纳为:找网站目前性能的瓶颈在哪里,把瓶颈解决了,其他方面细微的影响速度,也是很小的.如果不去找瓶颈,把细微而非主要的矛盾去解决了,但由于瓶颈没解决,整个速度也不会快.
四、实际操作的几大方面
依据http请求的原理,我一般习惯使用排除法,排除法依次为:网络带宽原因(包括距离远)、程序执行速度原因、数据库原因。这是依据一次http请求原理来定位的。
实际操作的几大方面,我总结如下:
- 要了解http请求原理
首先得了解一次http处理请求与响应的过程,不了解这种理论只能是瞎猜,猜b原因,猜c原因,然后用a试一试,无效(碰到运气了刚好有效),再用b试一试。
根据http协议,任何一次http请求它的处理过程如下:
1、 浏览器向服务器发出请求。此时浏览器一般左下角会显示:正在请求...
2、服务器接到请求后调用应用程序(php,java等,根据后缀名确定)执行。服务器接到请求后会马上返回一个头信息给浏览器,此时一般浏览器左下角会显示:已经连接到服务器(服务器接到请求)
ps:如果速度很快,浏览器左下角这些信息是一闪而过的。如果网络出现延迟,这些信息会一直显示在左下角位置。
3、php,java等代码执行完毕
4、代码执行完毕后,服务器会发一个http头响应浏览器(也就是告诉浏览器你开始接收数据了,准备好吧)。
此阶段浏览器在没有接到这个头信息之前,左下角会显示:正在等待服务器响应...
5、浏览器接到服务器发给响应头,开始接收了数据,浏览器处于接收数据状态,
我们会看到浏览器左下角会显示:正在传输数据....。也就是这个状态。
6、传输数据结束,完成一次http处理,整个过程http处理结束。
直觉上,根据浏览器左下角显示的状态信息,能够大体预估时间耗费在服务器端还是带宽层面。这需要了解http请求的过程方面知识。
不过,不是靠猜:感觉这个是性能问题,也改善一点点。这样子是多多益善,结果做白费工夫。要找到瓶颈,也就是主要矛盾,问题就容易解决。最好的定位瓶颈方法是用数据来进行侦测,有数据作为支持能够准确的看出瓶颈。具体点就是,放侦测代码,侦测每个环境耗费的时间多少秒,就能知道瓶颈。
浏览器下方还一直显示"等待响应...",说明服务器那边一直没有给予响应,浏览器一直处于等待服务器给予响应阶段。不涉及到带宽瓶颈(因为还没有到传输数据的阶段,在此之前就已经耗费比较长的时间)。利用这个排除法,把性能定位到服务器端去。于是,进入下一阶段,我要在服务器端写程序来统计程序的执行速度(服务端是php、java、.net都可以自己写代码实现,什么语言就用什么语言来实现)
浏览器归纳左下角的状态依次为:正在请求》》已连接到服务器》》等待响应》》服务器已响应》》正在传输数据
我想到几个试验来辅助说明浏览器左下角的状态:
1、 用个php文件(如果你是其他语言也可以照此进行),代码里面循环10万次。看浏览器怎么反应
因为php一直在执行,需要比较长的时间,所以没执行完,web服务器不会发响应头给浏览器。我们看到浏览器左下角的状态是”等待响应”
2、还记得需要长时间执行的php程序吗。我常常遇到数据库大数据量的时候,一条sql查询了很久,响应出现问题,所以php就一直在等待数据。那么web服务器(apache,nginx)根本没得到php的处理结果(也就是说php脚本耗费时间太多,根本没执行完)。此时在浏览器看到是”正在服务器等待响应"。
- 统计服务端的程序执行速度
1、自己写程序来统计程序执行耗时
服务器端,在php代码层面做一个速度统计:在程序开始执行之前记录一个时间点,结束的时候记录一个时间点。结束时间减去开始时间($end_time-$begin_time),就是php脚本的执行总耗时,这个时间其中包括了查询数据库,等待数据库响应的时间在内。
我在服务器端,对php程序的执行,做了执行耗费时长的统计,动态收集数据。也就不不能靠收集一两次的执行来判断。因为有的时候访问比较多,有的时候服务器状态比较好。网站运行是动态的,通俗的理解:你某个时候发现速度慢到需要5秒,等你去看的时候,速度又不慢了,1秒就打开了。因为速度还可以了,你都无法知道是哪里的原因。
所以,用数据来收集。收集100次,1000次总能了解哪几个页面速度慢、慢到耗时几秒钟。
方法比较简单:跟什么语言无关,不管是java、.net都差不多。在程序的开始和结束点记录时间,结束时间减去开始时间,就是程序的执行时间。把执行时间写到文本文件里面去。
这个程序需要自己来写的。
php的示范如下:
$begin_time = microtime_float();//执行开始记录时间戳,精确到毫秒 /*中间要执行的代码*/ end_time = microtime_float(); $exec_time = $end_time-$begin_time;//执行时间计算 @save_stat($exec_time);//把执行时间写入到文本文件中 /*下面是定义的两个函数*/ function save_stat($time) { static $call_count=1;//统计调用多少次 $call_limit = 10;//遇到并发问题的时候限制尝试多少次 if(!$time) return ; $exec_stat_file = "./exec_stat_file.txt"; $fp = fopen($exec_stat_file,'ab'); if(flock($fp,LOCK_EX)) { $s = 'access:'.date("Y-m-d H:i:s").',execute time:'.$time.'s,request_url:http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']."||".PHP_EOL; fwrite($fp,$s); flock($fp,LOCK_UN); }else{ usleep(1000); if($call_count<$call_limit) { $call_count++; save_stat($time); } } @fcolse($fp); //var_dump($fp); } function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return ((float)$usec + (float)$sec); }
下面是我上面统计程序收集的数据示范:
通过这种方式。基本上能够判断出,是不是php执行速度慢了。比如在浏览器打开一个网页,我基本上都需要等上5-6秒钟才能完全看到页面,我要判断是不是程序性能原因的话,我通过这个数据看出来。程序耗费时间都在2-5秒了,那么web服务器就需要等待这个时间后,才能给浏览器发响应头信息。这涉及到http请求原理,熟悉点这个方面很有必要,我根据浏览器左下角显示的文字信息来大体判断是不是前端原因,就是根据http请求过程来的。
2、定位哪个代码段耗费时长
在php程序方面。到底是哪个地方的代码耗费了比较长的时间了,是写得不好吗?不知道某个地方的代码,有专业的工具来定位一段php代码哪些地方各耗时长的。我不清楚java、.net方面的工具,应该都有。php方面的工具我所知道的有两个:一个是开源的xdebug,这个是一个调试php代码的工具。兼带有侦测代码性能统计的功能。另外一个是facebook公司的的xhprof,这个是他们公司自己开发的,因为facebook是用php开发的。xhprof如何使用,参考我原创的文章:http://www.cnblogs.com/wangtao_20/p/3320497.html。
这类工具的好处:精确到哪个函数执行用时多长时间,最耗费时间的代码在某处都能反应出来等等。cpu和内存耗费情况都能记录到的。我用这类工具能够判断程序的执行速度是否为瓶颈所在。
此方法还可以结合http原理来估算:看浏览器左下角的状态显示"正在等待响应..."耗时长,并且结合我放的统计执行时间长短的数据。
如果一直浏览器左下角卡在"正在传输数据....",那就不是服务器原因了,因为服务器此时已经发送响应给浏览器,浏览器处于接收数据状态。两点可能:传输距离远,或者带宽小。
- 如何定位瓶颈在数据库层面
这里有个知识点:比如php程序(其实java、.net也一样)从开始到结束耗费5秒中。实际上需要查询数据库获取数据,如果数据库压力比较大,程序其实一直在等待数据库给予数据,只有获取数据完毕后,才会接着执行后续代码。
由于上面脚本统计了程序执行耗费了多少时间,这个时间包括了从数据库获取数据的时间在内。
要想定位是数据库层面的,可以通过sql慢日志来记录,记录超过多少秒的sql记录到日志中去。这个在mysql中可以配置。参考:http://www.cnblogs.com/wangtao_20/p/3304645.html
我在实践中发现,服务器级别记录的只能作为参考,因为mysql服务器级别只能记录单条超过多少秒的sql。比如:一次页面会执行5条sql,每条sql都没有超过1秒啊,所以没有一条被记录到慢日志中去。或者一个页面变成是10条sql了
一条 0.1s,10*0.1 就是1秒了。
所以,我想知道a页面执行完毕,数据库层面查询数据到底耗费了多少时间(本质就是sql执行时间),怎么办?我要需要在程序方面做写代码进行统计。就是把每个页面执行的sql都记录下来,累加,就是a页面数据库层面耗费的时间。
我记得在a公司,遇到经常后台统计的功能,一点击就慢的问题,我当时没什么经验,使劲去优化php程序。想着是不是循环太多了,如果每次循环都需要去查询一次数据库获取数据。那么多少次循环,就需要多少次。这种方式是不是不好,改成一次查询获取所有数据。然后在程序端用php来聚合?
其实当时没有形成现在方法论。所以自己折腾了很多精力,毕竟我当时对后端优化没什么经历。毕业时间短。后来同学启发了我,不要去优化php程序了,你们的瓶颈在数据库层面。时间耗费在了php一直等待数据库给予数据。
教训:当时的方向应该是往数据库层面去优化。其实我感觉,只要定位瓶颈在数据库层面了,解决办法就进入一个新的领域了:数据库优化层面。数据库优化方法比较常规比如表结构调整(分表、大字段拆分)、索引等等方面去。
例子
一、 靠猜测、主观感觉。我在C公司的例子
我在c公司遇到两次速度慢的问题。
公司的生活服务网站速度慢了,集中在几个页面慢,不是所有页面都慢。老板反映这个问题,希望技术解决。网站用户和访问量很少。
这次,由我来解决这个问题。我的思路是要找到速度的瓶颈在哪里。我先不关注要不要做js和css压缩、数据库优化等。
同样还是在这个公司,第二次遇到网站速度慢的问题。老板开会反映给我们。一个运营A,他曾经做过程序开发,马上第一反应说:是带宽原因,因为我们网站购买的带宽只有1m。
散会后,其他部门在使用中也再次提到速度慢的问题,这位同事A,也在办公室,跟大家说是带宽小的原因。
我尽管知道,我们网站购买的带宽1m,不算大。但由于我没做数据统计,还没有定位到速度慢的瓶颈在哪里,是带宽,还是程序速度、还是数据库问题?基于我以前的经验,我都不敢随便去猜测,此时我一般不喜欢直接表明态度是哪里的原因。我要用数据在支持我的判断。
ps:根据我过去的经验和直接来判断,说是带宽未免太早,因为在此之前呆的两家公司我早就遇到过速度问题,是自己在错误中提升出来的方法。如果瓶颈不在带宽呢?就算把带宽加到4m都不会有改善。我其实也不喜欢这位同事嗷嗷叫,所以心里下决心,我会去私下里侦测一些网站速度的方面的数据来帮助我进行判断原因。那个时候就可以打他嘴巴的感觉。
不过,此次不是我来主导解决速度问题。因为会议期间,技术部有位同事B,他主动跟大家说由他来解决网站速度慢的问题。
B同事拥有将近20年的软件领域从业年限,他对于编码和设计模式方面有很丰富的经验。不过我发现他去解决网站速度的时候,他也没有抓瓶颈,他觉得html页面都不够整洁,往html结构调整和js、css压缩方面去下功夫。
虽然不是我负责弄速度这个工作,不过我觉得,这位同事也是抱着”看哪些需要优化,就做什么”的思路去改善的,比如html结构确实需要优化,去做这方面优化肯定使得网站只会往好的方面发展,不会往更差方面走。
我根据过去的所在公司的教训,我去观察了一下我们网站慢的的几个页面。还是会用到找瓶颈法,然后遵循的是“网络速度(http请求耗时)>程序速度>数据库速度”,其实是”由前端到后端的”逐个排除的思路。
首先、打开网页的时候,确实要5、6秒才完全打开一个页面。是不是网络带宽原因?
如果是带宽原因的话,是带宽不够,或者距离远吗?我注意观察了浏览器左下角,并不会马上看到”服务器已响应”的文字,左下角状态栏中,耗费时长的状态并不是在”正在传输数据….”。发现,耗费时长在于”等待响应…”
其实根据http请求和处理过程,就大体判断,”等待响应..”,服务器其实还没有给予响应。这个只是大体判断,我估算不是带宽原因。我不希望靠主管判断,希望用进一步的数据收集来做出定位。
因为网站不是一两个页面,这个同事B他费了2天时间做这方面优化、由于中途有其他事情。这方面也半停半工的状态。我觉得他的方向错误了。
我在服务器端,对php程序的执行,做了执行耗费时长的统计。
下面是我收集的数据。
还有一部分类似的数据我没有贴上。有的耗费时间5秒左右的。
可以看到,php程序执行时长都耗费了好几秒。瓶颈在程序的执行速度上。
通过这一步,可以推翻以前的判断了。
1、瓶颈根本不在带宽1m上,可以排除带宽的原因了。推翻了之前那个同事主观判断带宽原因。看来靠猜测,没有数据做支撑来优化是不行的。我看把带宽加到10m,这边程序执行速度也需要好几秒。能改善吗?瓶颈不在带宽。无用功!
2、也不在js和css文件的压缩、html结构优化上,这些不是主要矛盾。优化是可以,但后期去做。
因为,这个程序执行时长包括了操作数据库获取数据的时间(因为要等待嘛)。
那到底是不是数据库的瓶颈呢?结合数据库压力是不是够大,数据库的数据量多大。
其实
其实使用xhprof这个工具,能够知道那段代码耗费时间比较长了。想弄个图,可惜图丢了。
记得当时结果是:那段php代码重复调用文件缓存(把数据库数据缓存在文件中了)很多次,xhprof这个工具记录下了这个耗时点。两个页面的,xhprof工具当时统计出耗时5秒多和耗时3秒多的。
二、 b公司,带宽可以改善来提升
因为服务器在国外,距离比较远。公司网站本身不给国内人使用的,根本没法测验(国内访问国外,这么远距离肯定慢,很正常,不予理会)。但他们反映的是国外客户访问那个网站也慢。
记得b公司的同事把重点放在各种加速插件上面去,无改善。可是我用脚本统计程序执行时长,发现耗费时间基本上在1s以内,因为这种程序执行速度统计都已经包括了数据库查询的时间在内的了。基于http请求原理和程序执行速度的数据收集,我判断,在数据库优化、php程序性能方面都没有多大的优化空间。根本不是瓶颈所在。
我的方向集中在了:远距离和带宽层面的改善上面。而不是服务器本身性能和程序优化上。比如,因为有n个网站,此网站客户如果是针对英国的,那么租用的服务器最好安放到英国,不要跨到其他国家了。原因是:
1、 跨国的距离太远。远距离传输损耗快不到哪里去。
2、 国与国之间的网络肯定要比本国内各个省份之间的网络繁忙得多。毕竟此通道是一国所有向外数据的通道。比如中国与美国网络通信,是通过东部海底光缆联系的。意味着整个中国通向美国的数据访问都是经过同一道线路的。
尽管网站的访问量和数据量并不大,但瓶颈仍然定位准确了,从带宽和距离层面入手去解决能够突破瓶颈。
本文完