當解釋器完成對腳本代碼的分析後,便將它們生成能夠直接運行的中間代碼,也稱爲操做碼(Operate Code,opcode)。Opcode cache的目地是避免重複編譯,減小CPU和內存開銷。若是動態內容的性能瓶頸不在於CPU和內存,而在於I/O操做,好比數據庫查詢帶來的磁盤I/O開銷,那麼opcode cache的性能提高是很是有限的。可是既然opcode cache能帶來CPU和內存開銷的下降,這總歸是好事。php
這得從PHP代碼的生命週期提及,請求PHP腳本時,會通過五個步驟,以下圖所示:
數據庫
Zend引擎必須從文件系統讀取文件、掃描其詞典和表達式、解析文件、建立要執行的計算機代碼(稱爲Opcode),最後執行Opcode。每一次請求PHP腳本都會執行一遍以上步驟,若是PHP源代碼沒有變化,那麼Opcode也不會變化,顯然沒有必要每次都重行生成Opcode,結合在Web中無所不在的緩存機制,咱們能夠把Opcode緩存下來,之後直接訪問緩存的Opcode豈不是更快,啓用Opcode緩存以後的流程圖以下所示:
緩存
Opcode是一種PHP腳本編譯後的中間語言,就像Java的ByteCode,或者.NET的MSL,舉個例子,好比你寫下了以下的PHP代碼:服務器
< ?php echo "Hello World"; $a = 1 + 1; echo $a; ?>
PHP執行這段代碼會通過以下4個步驟(確切的來講,應該是PHP的語言引擎Zend)框架
1)Scanning(Lexing) ,將PHP代碼轉換爲語言片斷(Tokens)。 2)Parsing, 將Tokens轉換成簡單而有意義的表達式。 3)Compilation, 將表達式編譯成Opocdes。 4)Execution, 順次執行Opcodes,每次一條,從而實現PHP腳本的功能。
Optimizer+(Optimizer+於2013年3月中旬更名爲Opcache),OPcache經過將PHP腳本預編譯的字節碼存儲到共享內存中來提高PHP的性能,存儲預編譯字節碼的好處就是省去了每次加載和解析PHP腳本的開銷。ide
PHP 5.5.0 及後續版本中已經綁定了 OPcache 擴展。 對於 PHP 5.2,5.3 和 5.4 版本可使用 » PECL擴展中的OPcache庫。函數
PHP 5.5.0及後續版本性能
OPcache只能編譯爲共享擴展。若是你使用–disable-all參數禁用了默認擴展的構建,那麼必須使用–enable-opcache選項來開啓OPcache。編譯以後,就可使用 zend_extension 指令來將 OPcache 擴展加載到 PHP 中。測試
推薦的php.ini設置優化
使用下列推薦設置來得到較好的性能:
opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1 opcache.save_comments=0
你也能夠禁用 opcache.save_comments 而且啓用 opcache.enable_file_override。 須要提醒的是,在生產環境中使用上述配置以前,必須通過嚴格測試。 由於上述配置存在一個已知問題,它會引起一些框架和應用的異常, 尤爲是在存在文檔使用了備註註解的時候。
如下是opcache的配置說明,其中給有值得都是默認配置:
; opcache的開關,關閉時代碼再也不優化. opcache.enable=1 ; Determines if Zend OPCache is enabled for the CLI version of PHP opcache.enable_cli=1 ; OPcache的共享內存大小,以兆字節爲單位。總共可以存儲多少預編譯的PHP代碼(單位:MB) ; 推薦128 opcache.memory_consumption=64 ; 用來存儲臨時字符串的內存大小,以兆字節爲單位. ; 推薦8 opcache.interned_strings_buffer=4 ; 最大緩存的文件數目200到100000之間. ; 推薦4000 opcache.max_accelerated_files=2000 ; 內存"浪費"達到此值對應的百分比,就會發起一個重啓調度. opcache.max_wasted_percentage=5 ; 開啓這條指令, Zend Optimizer + 會自動將當前工做目錄的名字追加到腳本鍵上,以此消除同名文件間的鍵值命名衝突.關閉這條指令會提高性能,可是會對已存在的應用形成破壞. opcache.use_cwd=0 ; 開啓文件時間戳驗證 opcache.validate_timestamps=1 ; 檢查腳本時間戳是否有更新的週期,以秒爲單位。設置爲0會致使針對每一個請求,OPcache都會檢查腳本更新. ; 推薦60 opcache.revalidate_freq=2 ; 容許或禁止在include_path中進行文件搜索的優化. opcache.revalidate_path=0 ; 若是禁用,腳本文件中的註釋內容將不會被包含到操做碼緩存文件,這樣能夠有效減少優化後的文件體積,禁用此配置指令可能會致使一些依賴註釋或註解的應用或框架沒法正常工做,好比:Doctrine,Zend Framework2等. ; 推薦0 opcache.save_comments=1 ; 若是禁用,則即便文件中包含註釋,也不會加載這些註釋內容。本選項能夠和opcache.save_comments一塊兒使用,以實現按需加載註釋內容. opcache.load_comments=1 ; 打開快速關閉,打開這個在PHP Request Shutdown的時候會收內存的速度會提升. ; 推薦1 opcache.fast_shutdown=1 ; 容許覆蓋文件存在(file_exists等)的優化特性. opcache.enable_file_override=0 ; 定義啓動多少個優化過程. opcache.optimization_level=0xffffffff ; 啓用此Hack能夠暫時性的解決"can’t redeclare class"錯誤. opcache.inherited_hack=1 ; 啓用此Hack能夠暫時性的解決"can’t redeclare class"錯誤. ;opcache.dups_fix=0 ; 經過文件大小屏除大文件的緩存,默認狀況下全部的文件都會被緩存. ;opcache.max_file_size=0 ; 每N次請求檢查一次緩存校驗.默認值0表示檢查被禁用了,因爲計算校驗值有損性能,這個指令應當牢牢在開發調試的時候開啓. ;opcache.consistency_checks=0 ; 從緩存不被訪問後,等待多久後(單位爲秒)調度重啓. ;opcache.force_restart_timeout=180 ; 日誌記錄level,默認只有fatal error和error. ;opcache.error_log= ; 將錯誤信息寫入到服務器(Apache等)日誌 ;opcache.log_verbosity_level=1 ; 內存共享的首選後臺.留空則是讓系統選擇. ;opcache.preferred_memory_model= ; 運行php腳本時保護共享內存防止意外的寫入,只對debug時有用. ;opcache.protect_memory=0
最後說一下使用opcache加速php時應該注意的坑:
opcache依靠的是PHP文件的modify time做爲文件被修改的檢測條件,基於這個會引起兩個問題。
第一個問題是作版本回滾時,因爲版本回滾後的文件修改時間比現有opcache緩存的文件時間要往前一些,因此可能會致使opcache不會清除緩存,須要手動reload。
第二個問題是作版本發佈時,通常都是sync方式,可能會出現文件發佈一半時被opcache緩存,用戶訪問會報程序錯誤,這個主要是由於文件內容緩存了一半,可是文件的時間戳不會在改變,因此就算opcache檢測時也不會去讀取新的文件了,須要手動reload。
針對這兩個問題,不光reload能夠解決,一樣調用opcache的接口也能夠清除opcache緩存。
你可使用opcache_reset()或者或者opcache_invalidate()函數來手動重置OPcache。
opcache_reset():該函數將重置整個字節碼緩存,在調用opcache_reset()以後,全部的腳本將會從新載入而且在下次被點擊的時候從新解析。
opcache_invalidate():該函數的做用是使得指定腳本的字節碼緩存失效。 若是force沒有設置或者傳入的是FALSE,那麼只有當腳本的修改時間 比對應字節碼的時間更新,腳本的緩存纔會失效。
可是不推薦使用,我的在生產環境中進行代碼發佈後調用opcache_reset()清空緩存(測試確實能夠清空緩存),出現過奇葩問題(訪問量大的應用),後來就果斷放棄了,使用了reload的方式。