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的緩存,來避免重復的編譯??梢允褂肁PC,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函數的四倍。
數據庫連接當使用完畢時應關掉,不要用長連接。建議在連接之前,最好設置一下相應的超時機制,例如鏈接超時、讀寫超時、等待超時等。
錯誤消息代價昂貴。所以說在代碼測試完成,上線之前刪除錯誤信息報告代碼。
在方法中遞增局部變量,速度是最快的。幾乎與在函數中調用局部變量的速度相當。遞增一個全局變量要比遞增一個局部變量慢2倍。遞增一個對象屬性(如:$this->prop++)要比遞增一個局部變量慢3倍。遞增一個未預定義的局部變量要比遞增一個預定義的局部變量慢9至10倍。僅定義一個局部變量而沒在函數中調用它,同樣會減慢速度(其程度相當于遞增一個局部變量)。
派生類中的方法運行起來要快于在基類中定義的同樣的方法。
Apache解析一個PHP腳本的時間要比解析一個靜態HTML頁面慢2至10倍。盡量多用靜態HTML頁面,少用腳本。
除非腳本可以緩存,否則每次調用時都會重新編譯一次。引入一套PHP緩存機制通??梢蕴嵘?5%至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)的緩存也很有用,使得腳本不必為每個請求重新編譯。
參考
[1] 慕課網 – 性能優化之PHP優化總結筆記,視頻地址
[2] PHP 性能優化
[3] 關于 PHP 性能優化
[4] 一些PHP性能的優化
[5] 48條高效率的PHP優化寫法
總結
以上是生活随笔為你收集整理的PHP(四)——性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 集合类图(转)
- 下一篇: 【转】小程序图片裁剪组件