教育行业A股IPO第一股(股票代码 003032)

全国咨询/投诉热线:400-618-4000

PHP该如何优化性能?

更新时间:2018年11月16日15时49分 来源:传智播客 浏览次数:

本文是关于PHP性能优化、减少耗时方面的话题,虽然老生常谈,但还是以我个人的角度来一个总结或分享。

        网上关于50条PHP优化的方法,除此之外从架构或环境方面的优化建议等,是非常有益的。

       本文讲讲我所关注的一些方法或建议。

        一般来说,性能优化可先从大的方向开始考虑,从对影响性能比较大的因素来考虑,比如现在使用PHP5.7,性能据说可以成倍提高,最后考虑的应该是PHP语法细节上。

1. PHP部署环境
单台服务器常用apache+php和nginx+php-fpm方式部署,我们一直使用apache+php方式,据说现在用nginx+php-fpm部署方式性能比apache+php性能好,可考虑一试。另外就是像nginx+swoole等,也是可选项。
集群是在此基础上,使用nginx/lvs/云上lbs等反向代理作为负载均衡前端。PHP集群部署在可靠性的基础上,PHP集群处理性能比单台服务器有N倍提高(但作为服务的整体性能并不一定有N倍提升)。所以简单地可以认为,通过集群扩展服务器,可以使PHP服务性能得于提升。
不过,我们更关心的是单台PHP服务器性能优化提升,如何极尽所能。优化包括PHP所处的服务器、运行环境及其自身,但服务器内核配置、运行环境nginx/apache优化,本文不涉及。

2.PHP扩展使用
PHP扩展除了使用方便,还是提升性能的亲密伙伴。主要应用有三点:
1). 开启opcode的缓存,来避免重复的编译。可以使用APC,eAccelerator,XCache等PHP扩展,我们使用xcache。这种只要安装即可的事,为什么不用?
2). 使用扩展提供的方法(或PHP标准库的方法),扩展实现的效率比PHP代码中的高。但实际上满足我们项目的扩展方法有限,很多基础方法需要时一步封装,除非有能力自己开发扩展。可考虑使用扩展实现的PHP框架,如phalcon、yaf。
3). 本地缓存,也常用扩展来支持,比如xcache。本地可使用缓存扩展,缓存一些配置数据、元数据或主数据,不用每次都从数据库或文件中读取。
另,PHP版本上,现在可以考虑升到PHP7……

3. 文件加载和操作
这是非常重要的优化建议,尽量减少文件的读写,文件操作包括:文件读、判断文件是否存在、判断文件大小,特别是对于磁盘,减少文件操作即减少寻道时间,读取时间。减少一个文件操作,比优化N个CPU指令(request/request_once,echo/print,单引还是双引)、内存的性能效果要好得多。
实际应用中,关注以下几个地方:
1). 把.htaccess的内容写到apache配置中。平时我们都是通过.htaccess作为文件放置到PHP项目的根目录中,作为URL重写配置等。这就造成每次HTTP请求,都要先读取.htaccess,多了一次文件操作。通常.htaccess文件内容也不需要修改,因此可考虑在apache中配置,并禁用.htaccess文件。
2). PHP程序中,减少file_exists等文件操作函数的使用。在路由框架中,判断要引用的文件是否存在,如果不存在则显示错误,存在则执行文件里的类方法,如:
[php] view plain copy print?


  • if (file_exists($invoke ['path'])) {
  •      request $invoke ['path'];
  •      //执行类方法
  • }else{
  •     //显示错误
  • }



为什么不直接request $invoke ['path']呢?其实我们访问的路径(文件),正常的话都是存在的,所以没必要使用file_exists判断是否存在。但是访问到不存在的文件怎么呢?用set_error_handler方式全局处理。但是我只想对当前引用文件错误做特殊处理,把错误处理留在自己的框架中,不使用用户的全局的错误处理呢?可以这样 :
[php] view plain copy print?


  • $hd = set_error_handler(array($this, "page_not_found"));
  • require $invoke ['path'];
  • if($hd){
  •     set_error_handler($hd);
  • }



在require前,重新设置一个错误处理方法A,并会返回之前设置的错误处理方法;当require文件不存在时,会执行A,require正常时,重新把用户的error_handler设置回去。

还有一个就是日志的处理,可能每次操作日志都要判断一下日志是否存在(不存在即创建)和获取日志大小(分隔日志文件),一般来说,日志文件存在的可能性大,所以直接获取文件大小即可,通过获取日志文件大小一个方法filesize,同时可判断文件是否存在,不存在创建文件即可。这就减少一个文件操作方法的使用。如何使用filesize即可获取大小,又可判断文件是否存在,又不离开当前流程,正常执行下去呢?大家可以思考一下,因为filesize一个不存在的文件是会报错误的哦。
总之,只要是文件存在的可能性或者命中率高,就可考虑不要使用file_exists。


3). 类的加载中,使用精确加载并缓存,不要遍历目录文件的方式。如果在一个请求中,只加载且只加载一次需要的类文件是最好的。

4). 将文件缓存转成内存缓存。

4. 框架的选取
除了上面说的PHP扩展框架,还有很多其它非扩展实现的PHP框架,像thinkphp,laravel,这些框架是通用的,封装好、功能全,但自身会损耗一定的性能,主要原因我认为是加载的文件太多、定义的方法变量、检测的东西太多、执行的流程太长。但既然用了,这个得接受,能做的是在语法细节、代码逻辑上进行优化。仅就代码执行(没有外部调用, 数据库连接等),框架的执行时间远远大于应用代码执行时间,如果你还用上smarty模块引擎,性能必大打折扣。但是除了性能上考虑,还有很多其它因素值得利用这些成熟框架的。如果觉得性能是要考虑的方面,可以选择轻量一点、偶合性小的框架、或只选取需要的组件。

5.功能组件的使用
     组合使用,需要对整个框架有一个认识和把控。
1). 如果可以选用组件,第一个选择的就是路由组件了,能方便地路由到指定控制器方法即可,不要做多余的事,少用正则匹配,约定优于配置。
2). 类加载功能。上面说的,按需精确加载并缓存,一次请求有且只require一次。我们项目类的加载很原始,require_once and new。
3). 参数校验和安全处理。实际上这个也是相对耗费性能的过程,但为了安全还是有必要的。能做到按需参数处理最好,但是有时为方便,还是会在入口处全局处理安全过滤,参数校验就可以在控制器方法中处理。
4). session,简单地通过配置保存到Redis/memcache等缓存中,或者存到cookie中。但是根据默认的机制,session初始化(读取)和请求结束(写回),会产生两次网络操作。根据我们应用场景分析,session内容是不常变的,在不变的情况下可考虑只读而不写回(因此也就有不能更新session修改时间 的问题),这就要自己实现PHP会话的接口了,只有当有session修改时,变更的时候才回写session存储,这样,少一个网络操作可省多少时间啊。有些实现得不好的,在一个请求中多次连接或多次操作存储的,就不可取了。另外,结合cookieSession,即把session加密存到cookie中,对session的处理也会减少很多耗时。
5). 视图模板引擎。如果是API类接口服务,直接返回数据,如果是网页,那就需要使用模板引擎了。模板引擎也是很耗费性能的主,当然,喜欢原生态的方式,那是最高效的,像smarty这种,大而全,性能低效,而且很多功能我们都用不上。我们使用模板引擎,最核心的是html与PHP分离,然后才是变量与语法的处理。所以,要享受模板引擎的分离好处,又追求性能,那可以参考tmd_tpl。只实现分离,语法上使用PHP语法,因为如果要自己实现一套语法,需要大量的查找和替换。同样在模板引擎中,可以进一步合并多个视图文件为一个,减少文件操作等方式来优化。
6). 数据库方面。使用PDO,ORM使用方便但也有一定耗时,数组作为数据对象最高效,一般用短连接,使用单例连接对象或连接池(有些扩展可支持)。

0 分享到:
和我们在线交谈!