Zend OPcache 概念、安裝與設定:php.ini 的值與停用 JIT
Zend OPcache 是一個 PHP 擴充功能,會把 PHP 指令碼編譯成位元組碼並存放在共用記憶體中,讓同一個檔案再次執行時可以略過編譯。在使用 PHP 8.3 的 WordPress 伺服器上啟用 JIT 後,所有頁面都出現 500 錯誤,於是保留 OPcache 不動,只停用 JIT 來恢復。
從 PHP 5.5 起,OPcache 就預設包含在 PHP 中一併發布。
1️⃣ Zend OPcache 概念:位元組碼快取
每當有請求進來,PHP 指令碼都會經過三個階段:讀取檔案、編譯成位元組碼、執行。OPcache 運作時,從同一個檔案的第二次請求開始,前兩個階段會省掉,只剩下執行。
OPcache 用來存放位元組碼的共用記憶體,由 PHP-FPM 的所有工作程序共同使用。如果有多個網站連到同一個 PHP-FPM,這些網站的指令碼也會存放在同一塊共用記憶體中。
OPcache 儲存的是編譯後的 PHP 程式碼,所以每次開啟頁面時,PHP 的執行與資料庫查詢仍然會發生。把完成的 HTML 存起來、直接略過 PHP 執行,則是頁面快取的工作。
2️⃣ 確認 PHP OPcache 是否已安裝
OPcache 是否已安裝,可以從 [php -v] 輸出的最後一行確認。
php -vPHP 8.3.22 (cli) (built: Jun 3 2025 17:17:57) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.22, Copyright (c) Zend Technologies
with Zend OPcache v8.3.22, Copyright (c), by Zend Technologies如果沒有最後一行,就用 dnf 安裝。套件名稱通常是 php-opcache;如果是用 Remi 套件庫一次安裝了多個 PHP 版本,名稱會加上版本,例如 php83-php-opcache。
dnf -y install php-opcacheRemi 套件庫的 php-opcache 套件,會透過 [/etc/php.d/10-opcache.ini] 裡的 [zend_extension=opcache] 這一行載入擴充功能。安裝後重新啟動 PHP-FPM,OPcache 就會套用到網頁請求上。
3️⃣ OPcache 設定:php.ini 的值
OPcache 的設定可以放在兩個地方:[/etc/php.ini] 的 [opcache] 區段,以及 [/etc/php.d/10-opcache.ini]。
php --iniConfiguration File (php.ini) Path: /etc
Loaded Configuration File: /etc/php.ini
Scan for additional .ini files in: /etc/php.d
Additional .ini files parsed: /etc/php.d/10-opcache.ini,php.d 資料夾中的檔案會在 php.ini 之後讀取,所以同一個項目寫在兩個地方時,會套用 php.d 那邊的值。
下表是 PHP 8.3 的預設值,以及實際檢查的 WordPress 伺服器上套用的值。
| 項目 | PHP 8.3 預設值 | 套用值 | 說明 |
|---|---|---|---|
| opcache.enable | 1 | 1 | 使用 OPcache |
| opcache.enable_cli | 0 | 1 | 命令列 PHP(wp-cli 等)也會使用 |
| opcache.memory_consumption | 128 | 1024 | 存放位元組碼的共用記憶體(MB) |
| opcache.interned_strings_buffer | 8 | 16 | 相同字串只存一次的記憶體(MB) |
| opcache.max_accelerated_files | 10000 | 10000 | 可快取的鍵數量上限(實際上限是大於或等於此值的質數,設為 10000 時為 16229) |
| opcache.max_wasted_percentage | 5 | 10 | 記憶體不足且被浪費的空間超過此比例時,會清空快取並重新開始 |
| opcache.validate_timestamps | 1 | 1 | 檢查檔案是否有變更 |
| opcache.revalidate_freq | 2 | 60 | 檢查檔案變更的間隔(秒) |
| opcache.jit | tracing | disable | JIT 的方式(從 PHP 8.4 起預設為 disable) |
| opcache.jit_buffer_size | 0 | 0 | JIT 記憶體,設為 0 時 JIT 不會運作(從 PHP 8.4 起預設為 64M) |
放進 [/etc/php.ini] 的 [opcache] 區段的值如下。記憶體大小依伺服器記憶體決定,[opcache.max_accelerated_files] 則依 WordPress 的檔案數量決定。
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=1024
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.max_wasted_percentage=10
opcache.validate_timestamps=1
opcache.revalidate_freq=60如果在 php.ini 裡再加一行 [zend_extension],擴充功能會被載入兩次,並輸出 [Cannot load Zend OPcache] 警告。
[opcache.revalidate_freq] 為 60 時,修改 PHP 檔案後,舊的程式碼最多還可能再執行 60 秒。
PHP 檔案經常變動的 WordPress 伺服器,要把 [opcache.validate_timestamps] 設為 1,讓系統檢查檔案是否變更。Symfony 文件建議正式環境伺服器使用的 0,適合只有在部署新版本時才會更動 PHP 檔案的伺服器。
即使把 [opcache.validate_timestamps] 設為 0,在管理畫面進行更新時,WordPress 仍會自行清除已變更檔案的快取。但如果用 wp-cli 更新,或透過 SSH 修改檔案,在重新啟動 PHP-FPM 之前,仍會執行舊的程式碼。
4️⃣ 停用 OPcache 的 JIT
PHP 8 的 JIT 是讓大量 CPU 運算的工作變快的功能,所以對像 WordPress 這樣處理網頁請求的應用程式,效果很小。PHP JIT RFC 中刊載的 WordPress 測量結果也是如此:啟用 JIT 時每秒處理 326 個請求,停用時為 315 個。
在使用 PHP 8.3.22 的 WordPress 伺服器上,以 [opcache.jit=tracing] 與 [opcache.jit_buffer_size=128M] 啟用了 JIT。三天後,請求湧入、PHP-FPM 的工作程序多次重新啟動的那一天,所有頁面都發生了 500 錯誤。
錯誤記錄中留下了 [Allowed memory size of 1073741824 bytes exhausted] 這則訊息。接在後面的 [tried to allocate 4295229440 bytes],是在 PHP 記憶體上限 1GB 的情況下,一次想配置 4GB 的紀錄。停用 JIT 後,相同的請求就能正常處理了。
要停用 JIT,在 [/etc/php.d/10-opcache.ini] 加入下面兩行。即使 [/etc/php.ini] 裡還留著 JIT 設定,也會套用這個檔案的值。
opcache.jit=disable
opcache.jit_buffer_size=0PHP 8.3 的 [opcache.jit_buffer_size] 預設值是 0,所以沒有另外設定的話,JIT 不會運作。從 PHP 8.4 起,[opcache.jit] 的預設值也是 disable。
5️⃣ 套用設定並確認
修改後的設定要重新啟動 PHP-FPM 才會生效。
systemctl restart php-fpm重新啟動後,OPcache 的共用記憶體會被清空,並依新設定重新配置。
php -i | grep -E '^opcache\.(enable|memory_consumption|revalidate_freq|validate_timestamps|jit) 'opcache.enable => On => On
opcache.jit => disable => disable
opcache.memory_consumption => 1024 => 1024
opcache.revalidate_freq => 60 => 60
opcache.validate_timestamps => On => On[php -i] 輸出的值是命令列 PHP 的設定。在 RHEL 系列中,PHP-FPM 也讀取相同的 [/etc/php.ini] 與 [/etc/php.d] 檔案,所以值是一樣的。
在命令列呼叫 [opcache_get_status()],快取的指令碼會顯示為 0 個。命令列 PHP 每次執行都會重新建立共用記憶體,所以 PHP-FPM 的使用量要把下面的檔案放進網頁文件資料夾,再用瀏覽器確認。
<?php
header('Content-Type: text/plain');
$s = opcache_get_status(false);
print_r($s['memory_usage']);
print_r($s['opcache_statistics']);
print_r($s['interned_strings_usage']);
var_dump($s['cache_full']);[cache_full] 為 true 時,表示共用記憶體或鍵已經滿了。確認完之後,請立刻刪除這個檔案。
在三個 WordPress 網站共用同一個 PHP-FPM 的伺服器上,1024MB 共用記憶體中用掉了 317MB。每個網站快取的指令碼有 3,366 到 4,222 個,總共 11,358 個。
OPcache 以實際路徑儲存指令碼,對於用相對路徑載入的檔案,還會多建立一個附上工作目錄的鍵,所以鍵的數量會比指令碼數量多。指令碼有 11,358 個時,鍵填滿了 16,229 個的上限,16MB 的字串緩衝區也沒有剩餘空間。
鍵滿了之後,新的指令碼不會被快取,每次請求都要編譯。在 5 分 21 秒內,快取未命中從 18,408 次增加到 36,027 次,但快取的指令碼數量仍維持 11,358 個。
執行多個 WordPress 或外掛很多的伺服器,要把 [opcache.max_accelerated_files] 設得寬裕一些,避免鍵的上限被填滿。設為 20000 時,實際的鍵上限是 32,531 個。如果字串緩衝區已滿,也要一併調高 [opcache.interned_strings_buffer]。
🔢 FAQ & 推薦內容
ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。