php性能的问题
一.影响php性能的常见原因
1.php自身语法使用不当
2.php做了不擅长的时期()
3.php的周边环境(服务器Linux,磁盘:文件存储,数据库,缓存:内存,网络:带宽)
4.php自身的短板
5.未知问题
二、分析
PHP代码运行流程
*.php(PHP文件)->Scanner(扫描代码,分析)->Exprs(保存成zend引擎可以识别的文件)->Parser(解析)->Opcodes(能被执行的文件)->Exec(执行)->Output(输出结果)
1.php语言级的性能优化。指的是PHP语法基本功能,这部分优化比较简单易见、快速可行,比较快速看到效果。
1.1 少写PHP的代码,多用PHP自身能力解决问题
性能问题
自写代码冗余较多,可读性不佳,并且性能低,如代码很长很长...PHP代码越长PHP的执行效率越慢。
为什么性能低?
PHP代码需要解析编译为C语言,底层C语言又要编译成汇编语言机器语言才能执行,这个过程在每次请求过来之后都要处理一遍,所以开销很大(项目变大的话...)。
解决方法:
多使用PHP内置的变量、常量、函数。我们用PHP代码实现的功能和使用PHP内置的函数实现的同样功能差别是有的。
1.2 PHP内置函数的性能优劣。
情况描述
PHP内置函数之间依然存在快慢差别;少用PHP魔术方法;
建议:
多去了解PHP内置函数的执行实现复杂度。
测试方法:比较效率测试,如用microtime()函数,取差值,精确到毫秒级别;Linux的time命令可以查看开销。
1.3 产生额外开销的错误抑制符号“@”,最好别用(不管是性能优化和项目的健壮性等方面)。
@的逻辑是在代码前和代码结束后增加了Opcode,Opcode的作用就是忽略报错,其实就是相当于增加了error_reporting设置,等级报错为忽略(vld扩展可以查看被隐藏的Opcode)
1.4 合理使用内存
利用unset()及时释放不使用的内存,比如一些数据库多余字段(注意:unset()有时会出现注销不掉的情况)
当出现变量引用是,需要把两个变量都unset才可以注销内存;或者直接$变量=null来注销
1.5 尽量少用正则表达式
正则表达式的开销大,使用起来简单,但是性能低因为,正则表达式需要回溯;正则表达式越长,回溯的开销越大,优化正则表达式是需要技术水平的,正则技术不达标,不要乱用正则。
1.6 避免在循环内做运算。
循环内的计算式将被重复计算(我们在for循环或者while循环,会有重复计算,影响性能问题)
1.7 减少计算密集型业务
情况描述:
PHP不适合密集型(大数据量)运算的场景。
为什么?
PHP的语言特性决定PHP不适合做大数据量运算,PHP语言由C写的,PHP处于C基础之上,PHP的所有运算处理流程需要转化为C语言,PHP和C想比性能肯定输了,并且
PHP语言还有一些环境问题、语言特性,相比于C而言的开销要大很多的。PHP一段很长的代码,可能C很短就实现了...
PHP适合场景:
适合衔接WebServer与后端服务,WebServer来了请求交给PHP,PHP做一些校验、一些初始化数据处理,将请求转发交给后端,等待后台响应,后端可能是缓存、DB等其他业务,
后端响应之后,PHP再作为纽带,将信息传递给WebServer,这是PHP擅长的。PHP也擅长做UI呈现,也就是配合模板引擎做模板输出,其实就是一些字符串文本处理。
1.8 务必使用带引号字符串做键值(数组的Key字段)。
PHP会将没有使用引号的键值当做常量,产生查找常量的开销,如果查找到了常量有这个字符串,那么就把常量作为这个值了。
建议:严格使用引号作为键值,单引号即可。
1.9 尽量静态化
一个方法能被静态,就声明它为静态的,速度可以提高1/4,
其实静态方法和非静态方法的效率主要区别在内存:
静态方法在程序开始时生成内存,实例方法在程序运行中生成内存,所以静态方法可以直接调用,实例方法要先成生实例,通过实例调用方法,静态速度很快,但是多了会占内存。
任何语言都是对内存和磁盘的操作,至于是否面向对象,只是软件层的问题,底层都是一样的,只是实现方法不同。静态内存是连续的,因为是在程序开始时就生成了,而实例申请的是离散的空间,所以当然没有静态方法快。
静态方法始终调用同一块内存,其缺点就是不能自动进行销毁,而是实例化可以销毁。
1.10 用单引号代替双引号来包含字符串,这样做会更快一些。因为 PHP 会在双引号包围的 字符串中搜寻变量,单引号则不会
1.11 echo 比 print 快,并且使用 echo 的多重参数(译注:指用逗号而不是句点)代替字符串 连接
1.12 include 文件时尽量使用绝对路径,因为它避免了 PHP 去 include_path 里查找文件的速 度,解析操作系统路径所需的时间会更少
1.13 include()与require()的区别及require_once的代价
include() 遇到文件处理失败时,会产生一个警告;
而require()则会显示一个致命错误。也就是说在脚本执行过程中,如果代码中有错误,include()则会忽略错误并继续执行脚本,而require()则会停止脚本运行。
在php中使用require_once/include_once虽然方便,但是代价昂贵,据测试数据来看,require_once比require慢3-4倍,所以在php开发中,我们应该尽量使用require/include。
1.14 foreach 效率更高,尽量用 foreach 代替 while 和 for 循环;
总体来说,如果数据库过几十万了,才能看出来快一点还是慢一点,如果低于10万的循环,就不用测试了。php推荐用foreach。 循环数字数组时,for需要事先count($arr)计算数组长度,需要引入自增变量$i,每次循环都要进行条件判断$i<$c,然后自增$i++,输出数组元素时,$arr[$i]需要进行哈希操作. 而foreach循环数组时,指针会自动指向下一个元素,不需要计算数组长度,没有条件判断和自增变量,调用元素时也没有哈希操作,所以性能肯定要比for和while高.另外,for和while对存在键值映射的关联数组无能为力.
所以,遍历数组,首选foreach.foreach也是我PHP里最喜欢的关键字,因为它确实强大.array_map/array_filter/array_walk遍历数组的方式和foreach一样,但需要执行回调函数,所以也比foreach慢.
2.php周边环境的性能优化
性能排列:内存>数据库>磁盘>网络
2.1 减少文件的操作,操作完成要关闭文件。
2.2
网络优化:
网络原因:
1.对方接口不确定。接口地址错误;数据错误
2.网络不稳定
网络优化:
a.设置超时时间
1.链接超时 200ms
2.读超时 800ms
3.写超时 500ms
b.将串行请求并行化;把多次请求,放到一次请求中
1.使用curl_multi_*()
2.使用swoole扩展
2.3 压缩php接口
使用: Gzip ,php有对应压缩和解压的函数
利:利于我们的数据的数据输出,Client端能更快获取数据
弊:额外的cpu开销
2.4 缓存
对于变化少,访问量大的数据做缓存;缓存可以减轻服务器负载、减低网络阻塞、增强www可扩展性
a.文件缓存
b.memcached
c.Redis
2.5 旁路方案
3.php性能的测试
3.1 使用内置函数,测试代码执行时间;microtime()
3.2 Linux的time命令
3.3 apache测试工具,ab 可以测试并发时执行代码所需要的时间。