PHP的效能優化方法總結

王騰發表於2019-02-16

什麼情況之下,會遇到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作為後臺伺服器的系統中,經常會出現記憶體耗費太大的問題。

 

以下是我在其他博文收集的

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()掉;

相關文章