原理
首先,來看看 PHP 程式碼的執行過程:
- Lexing - 將 PHP 程式碼轉換為語言片段 (Tokens)
- Parse - 將 Tokens 轉換成簡單而有意義的表示式
- Compile - 將表示式編譯成位元組碼(OpCode)
- Excute - 順次執行位元組碼,每次一條,從而實現 PHP 指令碼的功能。
從執行步驟中可以看出,如果可以快取位元組碼的話,就可以跳過前面的步驟,從而大大提高 PHP 應用的效能。
OPcache
在 PHP 5.5 之前,不少擴充套件都實現了位元組碼的快取,比如 Zend 公司開發的 Zend Optimizer Plus。該擴充套件也隨著 PHP 5.5 一起釋出,並改名為 OPcache。也就是說,從 5.5 開始,OPcache 成了 PHP 的預設繫結擴充套件。該擴充套件通過將 PHP 指令碼預編譯的位元組碼儲存到共享記憶體中,省去了每次載入和解析 PHP 指令碼的開銷。
引數說明
OPcache 擴充套件可通過 php.ini
來進行配置,以下是一些常用的配置引數說明,後面對應的預設值。
opcache.memory_consumption = 128
分配的記憶體大小,如果應用的指令碼數較少的話,可適當調小。
opcache.interned_strings_buffer = 8
儲存預留字串的記憶體大小。PHP 直譯器在背後會找到相同字串的多個例項,把這個字串儲存在記憶體中,如果再次使用相同的字串,PHP 直譯器會使用指標。這麼做能節省記憶體。預設情況下,PHP 駐留的字串會隔離在各個 PHP 程式中。這個設定能讓 PHP-FPM 程式池中的所有程式把駐留字串儲存到共享的緩衝區中,以便在 PHP-FPM 程式池中的多個程式之間引用駐留字串。
opcache.max_accelerated_files = 10000
OPcache 雜湊表中可儲存的指令碼檔案數量上限。該值的設定要大於應用的檔案數量。
opcache.validate_timestamps = 1
該選項的值為 1 或者 0, 1 代表啟用,一旦啟用該項, OPcache 會每隔 revalidate_freq
設定的秒數來檢查指令碼是否更新,開發環境下,一般將其設定為 1。而生產環境下,一般將其設定為 0,然後通過重啟 Web 伺服器或者使用 PHP 提供的 opcache_reset()
或者 opcache_invalidate()
函式來手動重置 OPcache。
revalidate_freq = 2
指令碼更新的檢查時間週期,一旦將該值設定為 0,那麼每個請求 OPcache 都會檢查指令碼更新。由於生產環境下的 validate_timestamps
通常設定為 0,所以該值往往只在開發環境下有意義。
enable_cli = 0
僅針對 CLI 版本的 PHP 啟用操作碼快取,預設不開啟。
推薦配置
以下是 PHP 官方的推薦設定
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.enable_cli=1
以下是 《Modern PHP》一書的推薦配置
opcache.memory_consumption=64
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=4000
opcache.revalidate_freq=0
opcache.validate_timestamps 開發環境為 1,生產環境為 0
相關函式
說明
PHP 官方提供了與 OPcache 相關的函式
opcache_compile_file ( string $file ) : boolean
該函式可以用於在不用執行某個 PHP 指令碼的情況下,編譯該 PHP 指令碼並將其新增到位元組碼快取中去。 該函式可用於在 Web 伺服器重啟之後初始化快取,以供後續請求呼叫。
opcache_get_configuration ( void ) : array
該函式將返回快取例項的配置資訊。
opcache_get_status ([ boolean $get_scripts = TRUE ] ) : array
該函式將返回快取例項的狀態資訊。
opcache_invalidate ( string $script [, boolean $force = FALSE ] ) : boolean
該函式的作用是使得指定指令碼的位元組碼快取失效。 如果 force
沒有設定或者傳入的是 FALSE
,那麼只有當指令碼的修改時間 比對應位元組碼的時間更新,指令碼的快取才會失效。
opcache_is_script_cached ( string $file ) : bool
該函式用於檢測 PHP 指令碼是否已經被快取。
opcache_reset ( void ) : boolean
該函式將重置整個位元組碼快取。 在呼叫 opcache_reset()
之後,所有的指令碼將會重新載入並且在下次被點選的時候重新解析。有網友指出,該函式在 Cli 模式下不會生效。
應用
利用這些函式,可以方便的清空 OPcache 快取,比如將其封裝成指令碼,在程式碼更新後自動執行指令碼
#!/bin/bash
WEBDIR=/var/www/html/
RANDOM_NAME=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13)
echo "<?php opcache_reset(); ?>" > ${WEBDIR}${RANDOM_NAME}.php
curl http://localhost/${RANDOM_NAME}.php
rm ${WEBDIR}${RANDOM_NAME}.php
或者說利用這些函式來建立視覺化操作皮膚。例如,opcache-status
專案是一個展示 Opcache Status 的單個頁面,可以很方便的部署在自己的伺服器上。以下本地檢視的流程
$ git clone https://github.com/rlerdorf/opcache-status.git --depth=1
$ cd opcache-status
$ php -S localhost:8000 opcache.php
頁面顯示效果
參考資料