共計 4664 個字符,預計需要花費 12 分鐘才能閱讀完成。
php 性能怎么優化?性能是網站運行是否良好的關鍵因素,網站的性能與效率影響著公司的運營成本及長遠發展,編寫出高質高效的代碼是我們每個開發人員必備的素質, 也是我們良好的職業素養。
什么情況之下,會遇到 PHP 性能問題?
1:PHP 語法使用不恰當。
2:使用 PHP 語言做了它不擅長的事情。
3:使用 PHP 語言連接的服務不給力。
4:PHP 自身的短板(PHP 自身做不了的事情)。
5:我們也不知道的問題?(去探索、分析找到解決辦法,提升開發境界)。
對線上站點做壓力測試的時候,我們一定要將請求數和并發數,特別是并發數要設置的比較低,我們不能對線上的網站造成壓力問題,不管是自己的還是別人的。
PHP 性能問題一般不會超過占整個項目性能的 50%,一般在 30%~40%。
PHP 性能問題的解決方向,三個層級
1:PHP 語言級的性能優化,指的是 PHP 語法基本功能,這部分優化比較簡單易見、快速可行,比較快速看到效果。
a: 少寫 PHP 的代碼,多用 PHP 自身能力解決問題。
性能問題
自寫代碼冗余較多,可讀性不佳,并且性能低,如代碼很長很長 …PHP 代碼越長 PHP 的執行效率越慢。
為什么性能低?
PHP 代碼需要解析編譯為 C 語言,底層 C 語言又要編譯成匯編語言機器語言才能執行,這個過程在每次請求過來之后都要處理一遍,所以開銷很大(項目變大的話 …)。
解決方法:
多使用 PHP 內置的變量、常量、函數。我們用 PHP 代碼實現的功能和使用 PHP 內置的函數實現的同樣功能差別是有的。
b:PHP 內置函數的性能優劣。
情況描述:
PHP 內置函數之間依然存在快慢差別; 少用 PHP 魔術方法;
建議:
多去了解 PHP 內置函數的執行實現復雜度。
測試方法:比較效率測試,如用 microtime()函數,取差值,精確到毫秒級別;Linux 的 time 命令可以查看開銷。
c: 產生額外開銷的錯誤抑制符號“@”
最好別用(不管是性能優化和項目的健壯性等方面)。
@的邏輯是在代碼前和代碼結束后增加了 Opcode,Opcode 的作用就是忽略報錯,其實就是相當于增加了 error_reporting 設置,等級報錯為忽略(vld 擴展可以查看被隱藏的 Opcode);
d:合理使用內存。
情況描述:
PHP 有內存回收機制保底,但是也小心使用內存;
建議:
利用 unset()及時釋放不使用的內存,比如一些數據庫多余字段 (注意:unset() 有時會出現注銷不掉的情況)
e:盡量少用正則表達式。
情況描述:
正則表達式的開銷大,使用起來簡單,但是性能低因為,正則表達式需要回溯; 正則表達式越長,回溯的開銷越大,優化正則表達式是需要技術水平的,正則技術不達標,不要亂用正則。
f:避免在循環內做運算。
情況描述:
循環內的計算式將被重復計算(我們在 for 循環或者 while 循環,會有重復計算,影響性能問題)。
舉例:
錯誤用法:
$str = “hello world”;
for($i = 0; $i < strlen($str); $i ++){…}
正確用法:
$str = “hello world”;
$strlen = strlen($str);
for($i = 0; $i < $strlen; $i++){…}
g: 減少計算密集型業務
情況描述:
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 呈現,也就是配合模板引擎做模板輸出,其實就是一些字符串文本處理。
h:務必使用帶引號字符串做鍵值(數組的 Key 字段)。
情況描述
PHP 會將沒有使用引號的鍵值當做常量,產生查找常量的開銷,如果查找到了常量有這個字符串,那么就把常量作為這個值了。
建議:
嚴格使用引號作為鍵值,單引號即可。
2:PHP 周邊的性能優化:(PHP 前面有 WebServer,后面有數據庫)
3:PHP 語言自身的分析、優化(底層 C 級別的優化)
補充:
1: 盡量靜態化:
如果一個方法能被靜態,那就聲明它為靜態的,速度可提高 1 /4,甚至我測試的時候,這個提高了近三倍。
當然了,這個測試方法需要在十萬級以上次執行,效果才明顯。
其實靜態方法和非靜態方法的效率主要區別在內存:靜態方法在程序開始時生成內存, 實例方法在程序運行中生成內存,所以靜態方法可以直接調用, 實例方法要先成生實例, 通過實例調用方法,靜態速度很快,但是多了會占內存。
任何語言都是對內存和磁盤的操作, 至于是否面向對象, 只是軟件層的問題, 底層都是一樣的, 只是實現方法不同。靜態內存是連續的, 因為是在程序開始時就生成了, 而實例申請的是離散的空間, 所以當然沒有靜態方法快。
靜態方法始終調用同一塊內存,其缺點就是不能自動進行銷毀,而是實例化可以銷毀。
2. 銷毀變量去釋放內存,特別是大的數組;
數組和對象在 php 特別占內存的,這個由于 php 的底層的 zend 引擎引起的,
一般來說,PHP 數組的內存利用率只有 1/10, 也就是說,一個在 C 語言里面 100M 內存的數組,在 PHP 里面就要 1G。
特別是在 PHP 作為后臺服務器的系統中,經常會出現內存耗費太大的問題。
以下是我在其他博文收集的 php 性能優化方法:
1、如果能將類的方法定義成 static,就盡量定義成 static,它的速度會提升將近 4 倍。
2、$row[’id’] 的速度是 $row[id]的 7 倍。
3、注銷那些不用的變量尤其是大數組,以便釋放內存。
4、盡量避免使用__get,__set,__autoload。
5、require_once()代價昂貴。
6、include 文件時盡量使用絕對路徑,因為它避免了 PHP 去 include_path 里查找文件的速度,解析操作系統路徑所需的時間會更少。
7、如果你想知道腳本開始執行 (譯注:即服務器端收到客戶端請求) 的時刻,使用 $_SERVER[‘REQUEST_TIME’]要好于 time()
8、函數代替正則表達式完成相同功能。
9、str_replace 函數比 preg_replace 函數快,但 strtr 函數的效率是 str_replace 函數的四倍。
10、如果一個字符串替換函數,可接受數組或字符作為參數,并且參數長度不太長,那么可以考慮額外寫一段替換代碼,使得每次傳遞參數是一個字符,而不是只寫一行代碼接受數組作為查詢和替換的參數。
11、使用選擇分支語句 (譯注:即 switch case) 好于使用多個 if,else if 語句。
12、用 @屏蔽錯誤消息的做法非常低效,極其低效。
13、打開 apache 的 mod_deflate 模塊,可以提高網頁的瀏覽速度。
14、數據庫連接當使用完畢時應關掉,不要用長連接。
15、在方法中遞增局部變量,速度是最快的。幾乎與在函數中調用局部變量的速度相當。遞增一個全局變量要比遞增一個局部變量慢 2 倍。遞增一個對象屬性 (如:$this->prop++) 要比遞增一個局部變量慢 3 倍。遞增一個未預定義的局部變量要比遞增一個預定義的局部變量慢 9 至 10 倍。
16、僅定義一個局部變量而沒在函數中調用它,同樣會減慢速度(其程度相當于遞增一個局部變量)。PHP 大概會檢查看是否存在全局變量。
17、方法調用看來與類中定義的方法的數量無關,因為我 (在測試方法之前和之后都) 添加了 10 個方法,但性能上沒有變化。
18、派生類中的方法運行起來要快于在基類中定義的同樣的方法。
19、調用帶有一個參數的空函數,其花費的時間相當于執行 7 至 8 次的局部變量遞增操作。類似的方法調用所花費的時間接近于 15 次的局部變量遞增操作。
20、Apache 解析一個 PHP 腳本的時間要比解析一個靜態 HTML 頁面慢 2 至 10 倍。盡量多用靜態 HTML 頁面,少用腳本。
21、除非腳本可以緩存,否則每次調用時都會重新編譯一次。引入一套 PHP 緩存機制通常可以提升 25% 至 100% 的性能,以免除編譯開銷。
22、盡量做緩存,可使用 memcached。memcached 是一款高性能的內存對象緩存系統,可用來加速動態 Web 應用程序,減輕數據庫負載。對運算碼 (OP code)的緩存很有用,使得腳本不必為每個請求做重新編譯。
23、當操作字符串并需要檢驗其長度是否滿足某種要求時,你想當然地會使用 strlen()函數。此函數執行起來相當快,因為它不做任何計算,只返回在 zval 結構 (C 的內置數據結構,用于存儲 PHP 變量) 中存儲的已知字符串長度。但是,由于 strlen()是函數,多多少少會有些慢,因為函數調用會經過諸多步驟,如字母小寫化(譯注:指函數名小寫化,PHP 不區分函數名大小寫)、哈希查找,會跟隨被調用的函數一起執行。在某些情況下,你可以使用 isset() 技巧加速執行你的代碼。
24、當執行變量 $i 的遞增或遞減時,$i++ 會比 ++$i 慢一些。這種差異是 PHP 特有的,并不適用于其他語言,所以請不要修改你的 C 或 Java 代碼并指望它們能立即變快,沒用的。++$i 更快是因為它只需要 3 條指令 (opcodes),$i++ 則需要 4 條指令。后置遞增實際上會產生一個臨時變量,這個臨時變量隨后被遞增。而前置遞增直接在原值上遞增。這是最優化處理的一種,正如 Zend 的 php 優化器所作的那樣。牢記這個優化處理不失為一個好主意,因為并不是所有的指令優化器都會做同樣的優化處理,并且存在大量沒有裝配指令優化器的互聯網服務提供商(ISPs) 和服務器。
25、并不是事必面向對象(OOP),面向對象往往開銷很大,每個方法和對象調用都會消耗很多內存。
26、并非要用類實現所有的數據結構,數組也很有用。
27、盡量采用大量的 PHP 內置函數。
28、如果在代碼中存在大量耗時的函數,你可以考慮用 C 擴展的方式實現它們。
29、評估檢驗 (profile) 你的代碼。檢驗器會告訴你,代碼的哪些部分消耗了多少時間。Xdebug 調試器包含了檢驗程序,評估檢驗總體上可以顯示出代碼的瓶頸。
30、mod_zip 可作為 Apache 模塊,用來即時壓縮你的數據,并可讓數據傳輸量降低 80%。
31、在可以用 file_get_contents 替代 file、fopen、feof、fgets 等系列方法的情況下,盡量用 file_get_contents,因為他的效率高得多! 但是要注意 file_get_contents 在打開一個 URL 文件時候的 PHP 版本問題;
32、盡量的少進行文件操作,雖然 PHP 的文件操作效率也不低的;
33、優化 Select SQL 語句,在可能的情況下盡量少的進行 Insert、Update 操作(在 update 上,我被惡批過);
34、循環內部不要聲明變量,尤其是大變量:對象(這好像不只是 PHP 里面要注意的問題吧?);
35、多維數組盡量不要循環嵌套賦值;
36、在可以用 PHP 內部字符串操作函數的情況下,不要用正則表達式;
37、foreach 效率更高,盡量用 foreach 代替 while 和 for 循環;
38、用單引號替代雙引號引用字符串;
39、“用 i += 1 代替 i =i+1。符合 c /c++ 的習慣,效率還高”;
40、對 global 變量,應該用完就 unset()掉;