你有沒有開啟 OPcache 之後,命中率還是低於 10% 的經驗?這篇文章整理了我在管理多個 WordPress vhost 站台時,把 OPcache 命中率從不到 10% 調到穩定 99% 的完整過程,包含每個參數背後的邏輯、常見誤解(JIT 不是萬靈丹),以及多站台環境的記憶體分配策略。
OPcache 命中率是怎麼計算的
命中率的公式很直接:
命中率 = cache_hits ÷ (cache_hits + cache_misses) × 100%
每次 PHP 執行一個檔案,如果 bytecode 已經在記憶體裡,就算一次 hit;如果沒有(可能是第一次執行、被淘汰、或被判定為過期),就要重新編譯,算一次 miss。命中率偏低通常有三個原因:
- 剛重啟 PHP-FPM:快取是空的,所有請求都是 miss,樣本數不夠,數字沒有意義
- validate_timestamps = 1(預設值):每次請求都檢查檔案是否有變動,頻繁重驗導致 miss 增加
- 記憶體不夠:快取滿了,舊的 bytecode 被 LRU 淘汰,下次又要重新編譯
最關鍵的設定:validate_timestamps
這是命中率最大的單一影響因素。預設值 validate_timestamps=1 代表每次 PHP 執行時都去檢查檔案的修改時間,確認快取還沒過期。對於生產環境來說,這個動作完全沒有必要,PHP 檔案不會在每次請求之間自動改變。
; 生產環境設定(PHP-FPM reload 後才重新掃描)
opcache.validate_timestamps=0
opcache.revalidate_freq=0
關掉之後,OPcache 只在 PHP-FPM reload 或 restart 時重新掃描檔案。部署新版本外掛或主題後,記得執行 systemctl reload php-fpm 讓快取更新。
⚠️ 開發機不要關這個,否則改了 PHP 檔案會看不到效果。
記憶體配置:單站台 vs 多 vhost 差很多
OPcache 是以 PHP-FPM 行程為單位共享快取的。每個站台的 PHP 檔案路徑不同,bytecode 全部分開儲存。以 WordPress 為例,一個站台的核心加上常見外掛大約佔 50~80 MB;10 個站台就需要 500~800 MB。
| 站台數 | 建議 memory_consumption | 說明 |
|---|---|---|
| 1~3 個 | 128 MB | 單站或小型多站,綽綽有餘 |
| 5~10 個 | 512 MB | 中型多站環境標準值 |
| 10~20 個 | 1024 MB | 大型多 vhost 環境 |
| 20 個以上 | 1536~2048 MB | 建議同時監控 Free Memory |
判斷記憶體是否足夠最直接的方式是看 Free Memory。如果 Free Memory 接近 0,代表快取空間被撐滿,LRU 淘汰正在發生,命中率會開始下降。
完整推薦設定值(PHP 8.2 生產環境)
以下是我實際使用在多 vhost WordPress 主機上的設定,可以直接參考:
; === OPcache 生產環境推薦設定(PHP 8.2)===
; 記憶體大小:依站台數調整
opcache.memory_consumption=1024
; 最大快取檔案數:用質數避免 hash collision
opcache.max_accelerated_files=65407
; 字串緩衝區:WPML 多語系站建議拉高
opcache.interned_strings_buffer=64
; 關掉檔案時間戳驗證(生產環境專用)
opcache.validate_timestamps=0
opcache.revalidate_freq=0
; JIT:WordPress 是 I/O 密集型,效益不大,建議關閉
opcache.jit=disable
opcache.jit_buffer_size=0
JIT 要不要開?WordPress 的答案
PHP 8 推出 JIT(Just-In-Time 編譯器)後,很多教學都建議開啟,但這個建議是針對 CPU 密集型應用設計的,例如大量數值計算、影像處理或自訂演算法。WordPress 的瓶頸在資料庫查詢、外部 API 呼叫與 hook 執行順序,不在 CPU 計算。
| 情境 | JIT 效益 | 建議 |
|---|---|---|
| WordPress 前台頁面載入 | 幾乎無感(<3%) | 不值得開 |
| WooCommerce 結帳流程 | 可忽略 | 不值得開 |
| 自訂 PHP 計算邏輯 | 有感(10~30%) | 可以開 |
| 影像處理 / 報表產生 | 顯著提升 | 建議開 |
開啟 JIT 還要額外預占 jit_buffer_size 的記憶體(通常建議 64~128 MB),對多站台主機來說是額外成本。如果你的站台全是 WordPress,直接關閉 JIT,把那塊記憶體留給 OPcache 或 MySQL。
搭配 MySQL innodb_buffer_pool 一起調
OPcache 解決的是「PHP 編譯速度」的問題,但 WordPress 頁面慢,更多時候是 MySQL 的問題。innodb_buffer_pool_size 預設只有 128 MB,調高後 MySQL 把熱資料放在記憶體,不用一直讀磁碟,效果非常明顯。
先查 buffer pool 命中率,超過 99% 就不用動;低於 95% 代表記憶體不夠:
mysql -u root -p -e "
SELECT ROUND((1 - (
SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads'
) / (
SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_read_requests'
)) * 100, 2) AS buffer_pool_hit_rate_pct;"
如果你的伺服器有 16 GB RAM,建議這樣分配:
| 服務 | 建議分配 | 說明 |
|---|---|---|
| OPcache | 1 GB | 10~20 個 WordPress vhost |
| MySQL innodb_buffer_pool | 4~6 GB | 依 DB 實際大小調整 |
| PHP-FPM workers | 2 GB | 每 worker 約 40~80 MB |
| Nginx + 系統 | 0.5 GB | 保留 headroom |
更多 Rocky Linux LNMP 架構的調校細節,可以參考這篇:RockyLinux + PHP 8 安裝 PHP 擴充模組標準流程。
如何監控 OPcache 狀態
用 PHP 直接查詢最快:
<?php
$status = opcache_get_status(false);
if ($status && $status['opcache_enabled']) {
$stats = $status['opcache_statistics'];
echo "命中率:" . round($stats['opcache_hit_rate'], 2) . "%\n";
echo "Hits:" . number_format($stats['hits']) . "\n";
echo "Misses:" . number_format($stats['misses']) . "\n";
echo "已快取檔案數:" . $stats['num_cached_scripts'] . "\n";
echo "已用記憶體:" . round($status['memory_usage']['used_memory'] / 1048576, 2) . " MB\n";
echo "剩餘記憶體:" . round($status['memory_usage']['free_memory'] / 1048576, 2) . " MB\n";
}
看 Hits 和 Misses 的絕對數字比只看命中率百分比更有意義。如果 Hits 只有幾十筆,代表 PHP-FPM 剛重啟不久,樣本數太少,命中率偏低是正常的,等流量累積後再看。
常見問題 FAQ
OPcache 設定後要怎麼讓它生效?
修改 php.ini 後需要重啟或 reload PHP-FPM:systemctl reload php-fpm。如果使用 Apache,則重啟 Apache。設定生效後可用 opcache_get_status() 確認參數是否正確載入。
validate_timestamps=0 會不會讓外掛更新後沒效果?
會。關掉後 OPcache 不再自動偵測檔案變動,更新外掛或主題後需要手動執行 systemctl reload php-fpm(或呼叫 opcache_reset())讓快取更新。建議在部署流程中加上這個步驟。
多站台 vhost 要怎麼判斷 OPcache 記憶體夠不夠?
看 opcache_get_status() 裡的 memory_usage.free_memory,如果剩餘記憶體接近 0,代表快取空間已滿,LRU 淘汰正在發生,需要調高 opcache.memory_consumption。一般多站台環境每個 WordPress 站約佔 50~80 MB,10 個站台建議留 512 MB~1 GB。
PHP 8.2 的 JIT 到底要不要開?
對 WordPress 站台通常不建議。JIT 對 CPU 密集型運算有感,但 WordPress 的瓶頸在 DB 查詢與 I/O,JIT 幾乎不改善這類場景。開啟 JIT 還需要預占 jit_buffer_size 記憶體(建議至少 64 MB),對多 vhost 主機是額外負擔,效益比不上把記憶體留給 OPcache 或 MySQL。
命中率一直是 0% 或很低,是 OPcache 沒啟動嗎?
不一定。先確認 opcache_get_status()['opcache_enabled'] 是否為 true。若已啟動,命中率很低可能只是 PHP-FPM 剛重啟、快取是空的。等站台被訪問幾十次後再看 Hits 與 Misses 的絕對數字,若 Hits 已有幾百筆但命中率還低,才需要排查 validate_timestamps 設定。












