• PHP性能优化


    之前基于PHP开发的过程中,一直没有涉及到PHP性能优化的问题,但是一般来说PHP性能问题占整个项目性能问题一般占30%-50%部分,所以说,这部分内容是非常重要的。下面是最近自己PHP性能优化学习的资料整理。

    引言[1]
    PHP存在性能问题的情景?

    PHP语法使用不恰当
    使用了PHP语言他不擅长做的事情
    用PHP语言连接的服务不给力
    PHP自身的短板
    未知的问题
    PHP性能问题解决方向

    PHP语言级的性能优化:日常语法方法的优化。特点:简单高效很快见到效果。
    PHP周边问题的性能优化 :webserver,mysql。
    PHP语言自身的分析和优化 :PHP底层C语言逻辑的优化。
    从1~5的顺序,按照操作简单,见效快的指标进行的解决方案的排序。

    Apache压力测试软件

    Apache Benchmark,简称ab,是由Apache提供的压力测试软件,安装apache服务器时会自带压测软件。

    使用./ab -n100 -c10 http://www.baidu.com。
    其中-n请求数,-c并发数,url目标。

    ab 返回结果的参数

    Requests per second 每秒请求数 (优化目标 每秒的请求数尽可能多)
    Time per request 响应一个请求耗时 (优化目标 响应一个请求尽可能少)
    PHP语言级性能优化
    基本原则:少写php代码,多使用PHP内置的变量、常量、函数。

    性能问题:自写代码冗余较多,可读性不佳,并且性能低。php代码写的越长长执行效果就会越差,多用php自身的函数等

    因为PHP代码需要编译为C语言,C语言又会编译成汇编语言(机器语言),这里每一个过程都会请求一遍,开销很大。尤其是访问量大的时候,每次都会编译一遍。所以要尽量减少代码。

    PHP代码如何再linux解析的流程

    逐行扫描。*.php 通过zend引擎逐行扫描分析(Scanner)
    转码。保存成zend引擎自己能识别的语法(Exprs)
    解析为Opcodes。这些zend引擎能识别的语法,再解析(Parser)成Opcodes(最终要拿去执行的机器代码)。
    输出。执行Opcodes,然后输出。

    缓存服务都是缓存的Opcodes,不需要扫描和解析,

    PHP语言级性能优化建议

    PHP内置函数的性能优劣
    尽量使用更快内置函数,不同函数依然存在快慢差异。
    尽量少用魔法函数,魔法函数性能低。为了给程序员省事,php语言为你做了很多linux time函数 可以直接测试程序的耗时情况 魔法函数举例:__get();可以不用尽量不用,如果必须要用的时候再用。
    不使用@错误抑制符,改用try throw
    合理使用内存
    情况描述:php有内存回收机制保底,但也要小心使用内存
    建议:利用unset()及时释放不使用的内存(注:unset出现注销不掉的情况,自己查资料)
    尽量少的使用正则表达式
    情况描述:正则表达式性能低,因为正则表达式回溯开销较大
    好的建议:利用字符串处理函数,实现相同的逻辑
    避免循环内做运算
    情况描述:循环内的计算式会被重复计算
    例 for($i=0;$i<strlen($str);$i++) 每一次for循环都会进行计算strlen
    减少计算密集型业务
    情况描述:PHP不适合密集型运算场景
    原因:
    比如不适合大批量日志分析,或者大批量数据处理。
    php语言特性决定了PHP不适合做大数据运算
    php所有处理都需要转换成C语言,与C相比,C更好。
    php还有环境问题,还有语言特性。额外开销比C大很多。变量寄存等。
    PHP适合处理场景:适合衔接webserver与后端服务、UI呈现。(就是接口,简单数据处理,和套页面)
    务必使用带引号字符串做键值:php会将没有引号的键值检查一遍是不是常量,产生查询常量的开销
    PHP周边问题的性能优化
    PHP周边

    linux环境
    硬盘,文件存储,php读写
    数据库
    缓存:缓存是基于内存的
    网络
    PHP部署环境优化

    单台服务器常用apache+php和nginx+php-fpm方式部署,据说现在用nginx+php-fpm部署方式性能比apache+php性能好,可考虑一试。另外如nginx+swoole等,也是可选项。

    集群是在此基础上,使用nginx/lvs/云上lbs等反向代理作为负载均衡前端。PHP集群部署在可靠性的基础上,PHP集群处理性能比单台服务器有N倍提高(但作为服务的整体性能并不一定有N倍提升)。所以简单地可以认为,通过集群扩展服务器,可以使PHP服务性能得到提升。

    性能优化推荐

    减少文件类操作。常见PHP场景的开销次序:读写内存 < 读写数据库 < 读写磁盘 < 读写网络数据
    减少php发起网络请求,优化网络请求

    网络请求的坑:1对方接口的不确定因素 2 网络稳定性
    优化网络请求方法

    设置超时时间(建议值)

    连接超时 200ms 这是上限,最多也不能超过这个时间
    读超时 800ms 这个看具体情况
    写超时 500ms 建议不要超过500ms
    将串行请求并行化

    使用curl_multi_*() 返回时间是看用时最长的那个请求
    使用swoole扩展,通过C来进行并行化。(推荐使用)
    压缩PHP接口输出:如果用php做接口可通过使用Gzip压缩实现更高效的输出。压缩输出的利弊:
    利:利于数据输出,client能更快获取数据
    弊:额外的CPU开销。如果请求大,可能会有问题
    gzip如果数据量小于几十K的时候效果并不理想。如果大于100k,压缩就有效果。

    缓存重复计算内容:固定重复请求的数据做缓存。
    重叠时间窗口思想:串行变并行。如果后一个请求不强依赖于前一个返回值。就可以变成并行,降低总体时间消耗
    PHP语言自身的分析和优化
    PHP扩展使用[2],PHP扩展除了使用方便,还是提升性能的亲密伙伴。主要应用有三点:

    开启opcode的缓存,来避免重复的编译。可以使用APC,eAccelerator,XCache等PHP扩展,我们使用xcache。这种只要安装即可。

    使用扩展提供的方法(或PHP标准库的方法)。通过PHP扩展代替原PHP代码中高频的逻辑,扩展实现的效率比PHP代码中的高。但实际上满足我们项目的扩展方法有限,很多基础方法需要时一步封装,除非有能力自己开发扩展。可考虑使用扩展实现的PHP框架,如phalcon、yaf。

    本地缓存,也常用扩展来支持,比如xcache。本地可使用缓存扩展,缓存一些配置数据、元数据或主数据,不用每次都从数据库或文件中读取。

    另外,PHP版本上,可以考虑升到PHP7,PHP7在性能上有很大的提升。

    Runtime优化:HHVM[3](phpng也许更优于HHVM)
    具体的PHP语言级的优化建议[4]
    用单引号替代双引号引用字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会。

    如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍。

    $row[‘id’] 的速度是$row[id]的7倍。

    echo 比 print 快,并且使用echo的多重参数(译注:指用‘,’号而不是‘.’)代替字符串连接,比如echo $str1,$str2。区别用‘.’,先拼接,在整个输出;用‘,’,是挨个把三个变量输出。

    在执行for循环之前先确定最大循环数,不要每循环一次都计算最大值。

    1
    2
    // 不合理,每次循环都要计算
    for($i=0;$i<strlen($str);$i++)
    foreach效率更高,尽量用foreach代替while和for循环,如果考虑到foreach($array as $var)每次拷贝的消耗,可以使用foreach($array as &$var)这样的引用。

    尽量避免使用魔术变量,如__get,__set,__autoload。

    require_once()代价昂贵。require_once和include_once需要判重,因此效率上要低,但是5.2版本后效率问题已经基本解决。

    include文件时尽量使用绝对路径,因为它避免了PHP去include_path里查找文件的速度,解析操作系统路径所需的时间会更少。尽量少用iniset()来设置include_path。

    返回脚本开始执行(即服务器端收到客户端请求)的时刻,使用$_SERVER['REQUEST_TIME']要好于time()。因为$_SERVER['REQUEST_TIME']保存了发起该请求时刻的时间戳,而time()则返回当前时刻的Unix时间戳。

    函数代替正则表达式完成相同功能,字符串操作比正则替换要快。如strtok、strstr、strpos、str_replace、substr、explode、implode等等。注意不同的函数快慢也不同,str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。

    使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。因为php中switch支持数值和字符串变量,比C的switch要好用,建议使用。
    数据库连接当使用完毕时应关掉,不要用长连接。建议在连接之前,最好设置一下相应的超时机制,例如链接超时、读写超时、等待超时等。

    错误消息代价昂贵。所以说在代码测试完成,上线之前删除错误信息报告代码。

    在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。递增一个全局变量要比递增一个局部变量慢2倍。递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢3倍。递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。

    派生类中的方法运行起来要快于在基类中定义的同样的方法。

    Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍。尽量多用静态HTML页面,少用脚本。

    除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销。

    尽量可使用memcached等做缓存。memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载。对运算码(OPcode)的缓存很有用,使得脚本不必为每个请求做重新编译。

    当执行变量$i的递增或递减时,$i++会比++$i慢一些。这种差异是PHP特有的,并不适用于其他语言。++$i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令。后置递增实际上会产生一个临时变量,这个临时变量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如Zend的PHP优化器所作的那样。

    面向对象(OOP)是非必要的,因为面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。并非要用类实现所有的数据结构,数组也很有用。

    不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?不要过分迷恋各种设计模式,如上一条描述,过分的封装会带来性能的下降。需要考虑两者的权衡。Php有自己的特点,切不可东施效颦,过分效仿java的模式。

    分解成方法要适当,行数少使用频率高的方法尽量用直接写代码,可以减少函数堆栈开销;且方法嵌套不宜过深,否则大大影响PHP的运行效率。

    尽量采用大量的PHP内置函数,除去空函数调用的影响,内置函数和同样功能的C函数性能基本差不多。

    如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们。

    打开apache的mod_deflate模块,可以提高网页的浏览速度。mod_zip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%。

    在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情况下,尽量用file_get_contents,因为他的效率高得多!但是要注意file_get_contents在打开一个URL文件时候的PHP版本问题。这个要记住,尽量使用file_get_contents和file_put_contents,不需要自己判断文件句柄打开是否成功。

    尽量的少进行文件操作,虽然PHP的文件操作效率也不低的;

    优化Select SQL语句,在可能的情况下尽量少的进行Insert、Update操作(在update上,我被恶批过);

    循环内部不要声明变量,尤其是大变量:对象(这好像不只是PHP里面要注意的问题吧?)。这个必须的,变量过多或者过大时,每次重分配的开销就无法忽略。

    多维数组尽量不要循环嵌套赋值;

    对global变量,应该用完就unset()释放掉;

    当操作字符串并需要检验其长度是否满足某种要求时,使用isset()替代strlen()函数。isset()作为一种语言结构,它的执行不需要函数查找和字母小写化。此函数执行起来相当快,只返回在zval结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于strlen()是函数更慢,因为函数调用会经过诸多步骤,如字母小写化(指函数名小写化,PHP不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用isset() 技巧加速执行你的代码。   

    1
    2
    3
    4
    // (举例如下)
    if (strlen($foo) < 5) { echo “Foo is too short”$$ }
    // (与下面的技巧做比较)
    if (!isset($foo{5})) { echo “Foo is too short”$$ }
    评估检验(profile)你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈。

    函数相关信息保存在一个大的hash_table中,每次调用时通过函数名在hash表中查找,因此函数名长度对性能也有一定影响。

    函数不宜嵌套过深,递归使用要谨慎。

    如不是特殊需要,参数传递都建议使用传值而不是传引用。当然,如果参数是很大的数组且需要修改时可以考虑引用传递。

    使用NoSQL、Memchached或者Redis缓存。这些是高性能的分布式内存对象缓存系统,能提高动态网络应用程序性能,减轻数据库的负担。这对运算码 (OPcode)的缓存也很有用,使得脚本不必为每个请求重新编译。

  • 相关阅读:
    一个将配置文件转换成xml的示例程序
    DatagridView控件加CheckBox
    Sql Server Split函数
    SQL语句压缩数据库和Log
    NPOI、OpenXML SDK、OpenOffice SDK 操作Excel
    判断参数对象是否为DBNULL
    软件开发模型
    c# ComboBox禁用鼠标滚轮
    动态管理视图和函数的使用
    TreeView 在失去焦点的时候 选中的TreeNode仍为高亮
  • 原文地址:https://www.cnblogs.com/liliuguang/p/14474159.html
Copyright © 2020-2023  润新知