本文是關于PHP性能優(yōu)化、減少耗時方面的話題,雖然老生常談,但還是以我個人的角度來一個總結(jié)或分享。
網(wǎng)上關于50條PHP優(yōu)化的方法,除此之外從架構(gòu)或環(huán)境方面的優(yōu)化建議等,是非常有益的。
本文講講我所關注的一些方法或建議。
一般來說,性能優(yōu)化可先從大的方向開始考慮,從對影響性能比較大的因素來考慮,比如現(xiàn)在使用PHP5.7,性能據(jù)說可以成倍提高,最后考慮的應該是PHP語法細節(jié)上。
1. PHP部署環(huán)境
單臺服務器常用apache+php和nginx+php-fpm方式部署,我們一直使用apache+php方式,據(jù)說現(xiàn)在用nginx+php-fpm部署方式性能比apache+php性能好,可考慮一試。另外就是像nginx+swoole等,也是可選項。
集群是在此基礎上,使用nginx/lvs/云上lbs等反向代理作為負載均衡前端。PHP集群部署在可靠性的基礎上,PHP集群處理性能比單臺服務器有N倍提高(但作為服務的整體性能并不一定有N倍提升)。所以簡單地可以認為,通過集群擴展服務器,可以使PHP服務性能得于提升。
不過,我們更關心的是單臺PHP服務器性能優(yōu)化提升,如何極盡所能。優(yōu)化包括PHP所處的服務器、運行環(huán)境及其自身,但服務器內(nèi)核配置、運行環(huán)境nginx/apache優(yōu)化,本文不涉及。
2.PHP擴展使用
PHP擴展除了使用方便,還是提升性能的親密伙伴。主要應用有三點:
1). 開啟opcode的緩存,來避免重復的編譯??梢允褂肁PC,eAccelerator,XCache等PHP擴展,我們使用xcache。這種只要安裝即可的事,為什么不用?
2). 使用擴展提供的方法(或PHP標準庫的方法),擴展實現(xiàn)的效率比PHP代碼中的高。但實際上滿足我們項目的擴展方法有限,很多基礎方法需要時一步封裝,除非有能力自己開發(fā)擴展??煽紤]使用擴展實現(xiàn)的PHP框架,如phalcon、yaf。
3). 本地緩存,也常用擴展來支持,比如xcache。本地可使用緩存擴展,緩存一些配置數(shù)據(jù)、元數(shù)據(jù)或主數(shù)據(jù),不用每次都從數(shù)據(jù)庫或文件中讀取。
另,PHP版本上,現(xiàn)在可以考慮升到PHP7……
3. 文件加載和操作
這是非常重要的優(yōu)化建議,盡量減少文件的讀寫,文件操作包括:文件讀、判斷文件是否存在、判斷文件大小,特別是對于磁盤,減少文件操作即減少尋道時間,讀取時間。減少一個文件操作,比優(yōu)化N個CPU指令(request/request_once,echo/print,單引還是雙引)、內(nèi)存的性能效果要好得多。
實際應用中,關注以下幾個地方:
1). 把.htaccess的內(nèi)容寫到apache配置中。平時我們都是通過.htaccess作為文件放置到PHP項目的根目錄中,作為URL重寫配置等。這就造成每次HTTP請求,都要先讀取.htaccess,多了一次文件操作。通常.htaccess文件內(nèi)容也不需要修改,因此可考慮在apache中配置,并禁用.htaccess文件。
2). PHP程序中,減少file_exists等文件操作函數(shù)的使用。在路由框架中,判斷要引用的文件是否存在,如果不存在則顯示錯誤,存在則執(zhí)行文件里的類方法,如:
[php] view plain copy print?
- if (file_exists($invoke ['path'])) {
- request $invoke ['path'];
- //執(zhí)行類方法
- }else{
- //顯示錯誤
- }
為什么不直接request $invoke ['path']呢?其實我們訪問的路徑(文件),正常的話都是存在的,所以沒必要使用file_exists判斷是否存在。但是訪問到不存在的文件怎么呢?用set_error_handler方式全局處理。但是我只想對當前引用文件錯誤做特殊處理,把錯誤處理留在自己的框架中,不使用用戶的全局的錯誤處理呢?可以這樣 :
[php] view plain copy print?
- $hd = set_error_handler(array($this, "page_not_found"));
- require $invoke ['path'];
- if($hd){
- set_error_handler($hd);
- }
在require前,重新設置一個錯誤處理方法A,并會返回之前設置的錯誤處理方法;當require文件不存在時,會執(zhí)行A,require正常時,重新把用戶的error_handler設置回去。
還有一個就是日志的處理,可能每次操作日志都要判斷一下日志是否存在(不存在即創(chuàng)建)和獲取日志大?。ǚ指羧罩疚募?,一般來說,日志文件存在的可能性大,所以直接獲取文件大小即可,通過獲取日志文件大小一個方法filesize,同時可判斷文件是否存在,不存在創(chuàng)建文件即可。這就減少一個文件操作方法的使用。如何使用filesize即可獲取大小,又可判斷文件是否存在,又不離開當前流程,正常執(zhí)行下去呢?大家可以思考一下,因為filesize一個不存在的文件是會報錯誤的哦。
總之,只要是文件存在的可能性或者命中率高,就可考慮不要使用file_exists。
3). 類的加載中,使用精確加載并緩存,不要遍歷目錄文件的方式。如果在一個請求中,只加載且只加載一次需要的類文件是最好的。
4). 將文件緩存轉(zhuǎn)成內(nèi)存緩存。
4. 框架的選取
除了上面說的PHP擴展框架,還有很多其它非擴展實現(xiàn)的PHP框架,像thinkphp,laravel,這些框架是通用的,封裝好、功能全,但自身會損耗一定的性能,主要原因我認為是加載的文件太多、定義的方法變量、檢測的東西太多、執(zhí)行的流程太長。但既然用了,這個得接受,能做的是在語法細節(jié)、代碼邏輯上進行優(yōu)化。僅就代碼執(zhí)行(沒有外部調(diào)用, 數(shù)據(jù)庫連接等),框架的執(zhí)行時間遠遠大于應用代碼執(zhí)行時間,如果你還用上smarty模塊引擎,性能必大打折扣。但是除了性能上考慮,還有很多其它因素值得利用這些成熟框架的。如果覺得性能是要考慮的方面,可以選擇輕量一點、偶合性小的框架、或只選取需要的組件。
5.功能組件的使用
組合使用,需要對整個框架有一個認識和把控。
1). 如果可以選用組件,第一個選擇的就是路由組件了,能方便地路由到指定控制器方法即可,不要做多余的事,少用正則匹配,約定優(yōu)于配置。
2). 類加載功能。上面說的,按需精確加載并緩存,一次請求有且只require一次。我們項目類的加載很原始,require_once and new。
3). 參數(shù)校驗和安全處理。實際上這個也是相對耗費性能的過程,但為了安全還是有必要的。能做到按需參數(shù)處理最好,但是有時為方便,還是會在入口處全局處理安全過濾,參數(shù)校驗就可以在控制器方法中處理。
4). session,簡單地通過配置保存到Redis/memcache等緩存中,或者存到cookie中。但是根據(jù)默認的機制,session初始化(讀?。┖驼埱蠼Y(jié)束(寫回),會產(chǎn)生兩次網(wǎng)絡操作。根據(jù)我們應用場景分析,session內(nèi)容是不常變的,在不變的情況下可考慮只讀而不寫回(因此也就有不能更新session修改時間 的問題),這就要自己實現(xiàn)PHP會話的接口了,只有當有session修改時,變更的時候才回寫session存儲,這樣,少一個網(wǎng)絡操作可省多少時間啊。有些實現(xiàn)得不好的,在一個請求中多次連接或多次操作存儲的,就不可取了。另外,結(jié)合cookieSession,即把session加密存到cookie中,對session的處理也會減少很多耗時。
5). 視圖模板引擎。如果是API類接口服務,直接返回數(shù)據(jù),如果是網(wǎng)頁,那就需要使用模板引擎了。模板引擎也是很耗費性能的主,當然,喜歡原生態(tài)的方式,那是最高效的,像smarty這種,大而全,性能低效,而且很多功能我們都用不上。我們使用模板引擎,最核心的是html與PHP分離,然后才是變量與語法的處理。所以,要享受模板引擎的分離好處,又追求性能,那可以參考tmd_tpl。只實現(xiàn)分離,語法上使用PHP語法,因為如果要自己實現(xiàn)一套語法,需要大量的查找和替換。同樣在模板引擎中,可以進一步合并多個視圖文件為一個,減少文件操作等方式來優(yōu)化。
6). 數(shù)據(jù)庫方面。使用PDO,ORM使用方便但也有一定耗時,數(shù)組作為數(shù)據(jù)對象最高效,一般用短連接,使用單例連接對象或連接池(有些擴展可支持)。
|