WordPress VPS 設定 Redis 物件快取
在自己的 VPS 為 WordPress 設定 Redis 物件快取:綁定 localhost、設定 maxmemory 與淘汰策略,並用 WP-CLI 驗證快取確實運作。
WordPress 的 Redis 物件快取功能
WordPress 的 Redis 物件快取會將資料庫查詢結果儲存在記憶體中,讓下一次請求直接從 Redis 讀取,而不必再次向 MySQL 查詢。WordPress 核心本身已有物件快取,WP_Object_Cache,但該快取位於 PHP 記憶體中,請求結束時就會被清除。drop-in 檔案會以可連線至 Redis 的實作取代原本的物件快取,讓快取內容可保留至下一次請求。
物件快取不是頁面快取。這項差異會決定本指南是否值得閱讀。頁面快取會儲存 URL 產生的完整 HTML,之後直接提供該 HTML,不必執行 PHP。這比 Redis 能做到的速度更快,也適用於尚未登入的訪客。使用者登入、將商品加入購物車或開啟管理介面後,頁面快取就不再介入,WordPress 會完整處理請求:bootstrap、外掛與查詢。物件快取則能降低這類請求的成本。它適用於頁面快取無法處理的流量,包括已登入工作階段、購物車、結帳與 wp-admin。在 WooCommerce 商店中,這通常是大部分成本高昂的流量。
兩者可以並用,忙碌的網站通常也應同時使用。請先釐清要解決的問題。以匿名讀者為主的形象網站,幾乎所有效能提升都來自頁面快取;加入 Redis 通常不會帶來明顯變化。
開始前,先了解一項限制。物件快取不會讓緩慢的查詢變快。它只會避免重複執行已經完成的查詢。快取未命中後的第一次請求仍須支付完整成本,因此,執行未建立索引之查詢的外掛,在每個快取生命週期內仍會執行該查詢一次。
執行前的必要條件
- 具備 shell 與
sudo的 Linux VPS。不需要控制面板。 - 由 PHP-FPM 提供服務的 WordPress,例如執行於 Ubuntu 24.04 上的 LAMP stack。
- 伺服器上已安裝 WP-CLI。這裡的每個步驟都有管理介面中的對應操作,但使用 shell 會更快。
- Redis 與 PHP 位於同一台機器。這項設定的重點就是低延遲,經過網路跳轉會抵銷這項優勢。
以下指令適用於搭配 PHP 8.3 與 www-data Web 使用者的 Ubuntu 24.04。請依伺服器環境調整 PHP 版本與使用者。請在 WordPress 目錄中執行 wp 指令,也就是包含 wp-config.php 的目錄。
安裝 Redis 與 PHP 擴充功能
sudo apt update
sudo apt install -y redis-server php-redis
sudo systemctl enable --now redis-server
redis-cli pingredis-cli ping 應回應 PONG。如果輸出 Could not connect to Redis at 127.0.0.1:6379: Connection refused,表示伺服器尚未執行,請先查看 systemctl status redis-server,再繼續後續操作。
php-redis 是 PhpRedis,也就是 PECL 提供的 C 擴充功能。它的速度比純 PHP 實作的 Predis 快;只要存在,該外掛就會自動使用它。PHP-FPM 會在啟動時載入擴充功能,因此新增擴充功能後,必須重新啟動 pool 才會生效。
sudo systemctl restart php8.3-fpm
php -m | grep redis請注意最後一項檢查:php -m 列出的是命令列 PHP 載入的模組,而 FPM 可能載入不同的模組集合。真正具判定作用的是該外掛本身的診斷結果,位置在後文。
截至 2026 年 8 月,Ubuntu 24.04 提供 Redis 7.0.15,作為物件快取已足夠。如果需要較新的版本,Redis 也提供自己的 APT repository。
sudo apt install -y lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt update
sudo apt install -y redis如果你的發行版提供 Valkey,這是 2024 年授權變更後建立的 fork,則它使用相同的通訊協定,以下所有內容都可直接套用。
Bind Redis so nothing else can reach it
Redis has no password by default. Anything that can open a connection to port 6379 can read every cached value and run FLUSHALL. Instances exposed to the internet get found by scanners within hours, so the network setting comes before the tuning.
Open /etc/redis/redis.conf and confirm these lines:
bind 127.0.0.1 -::1
protected-mode yesThen check what is really listening, because the config file is a claim and ss is the evidence.
sudo ss -lntp | grep 6379127.0.0.1:6379 is what you want. 0.0.0.0:6379 means Redis is answering on the public interface: fix the bind line and restart.
When PHP and Redis sit on the same box, a Unix socket is better than loopback TCP. There is no TCP stack in the path, and access is decided by file permissions instead of by a firewall rule you might later change.
unixsocket /run/redis/redis-server.sock
unixsocketperm 770The socket is owned by the redis user and group, so the web user has to join that group.
sudo systemctl restart redis-server
sudo usermod -aG redis www-data
sudo systemctl restart php8.3-fpm
redis-cli -s /run/redis/redis-server.sock pingThat must also print PONG. Could not connect to Redis at /run/redis/redis-server.sock: Permission denied means the group did not take effect. Check id www-data, and remember a running PHP-FPM keeps the groups it had at start, which is why the restart is in the list. Leave TCP enabled until the socket is proven, or a typo takes away both paths at once.
Redis 應配置多少記憶體?
請根據自己的伺服器計算數值。沒有設定 maxmemory 的 Redis 會持續成長,直到 kernel 耗盡記憶體並由 OOM killer 終止某個程序。通常被終止的是佔用記憶體最大的程序;在 WordPress 伺服器上,這通常是 MySQL。journalctl -k | grep -i "out of memory" 只能在事後顯示這次終止事件,而網站此時通常已經中斷。
先從總 RAM 開始扣除。MySQL 或 MariaDB 會保留 innodb_buffer_pool_size,另加每個連線的緩衝區。PHP-FPM 的記憶體需求是 pm.max_children 乘以單一 worker 的實際常駐記憶體大小。在外掛較多的網站上,通常是 64 MB 到 128 MB。kernel 與 web server 需要幾百 MB。剩餘記憶體就是上限,Redis 只能使用其中一部分。
1 個執行單一商店網站的 4 GB VPS 預算範例
以下數值僅供示範,並非來自你的伺服器。請將每個數值替換成你的伺服器回報的值。
- MariaDB 使用 1 GB buffer pool:1024 MB
- PHP-FPM,10 個 worker,每個 96 MB:960 MB
- kernel、nginx 或 Apache、sshd、日誌:512 MB
- 剩餘:約 1.5 GB
在這種情況下,maxmemory 設為 256 MB 是合理的起始值。這會保留足夠的餘裕,單一 WordPress 網站通常也不需要更多記憶體。
現在請實際測量,不要猜測。經過一天的實際流量後:
redis-cli info memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'
redis-cli dbsize如果 used_memory_human 明顯低於你的上限,請降低上限,將 RAM 交還給 MySQL,因為 MySQL 能更有效地使用這些記憶體。如果它持續達到上限,且 evicted_keys 整天上升,請提高上限。在 /etc/redis/redis.conf 中設定該值。
maxmemory 256mb
maxmemory-policy allkeys-lruredis-cli config set maxmemory 256mb 只會立即套用,下一次重新啟動時就會遺失,這與單獨使用 sysctl -w 有相同陷阱。請編輯檔案,然後執行 sudo systemctl restart redis-server,再讀回該值確認。再設定一道限制會更安全:在 systemd unit 上設定 MemoryMax 上限,可避免設定錯誤的 Redis 使整台伺服器中斷。請將它設在 maxmemory 以上,不要設為相同值,因為 cgroup 限制會終止程序,而不是逐出 key。如果 Redis 與 WordPress 一起執行在 container 中,則相同的數值應填入 Compose 檔案中的記憶體限制;選擇 在 Docker 中或主機上執行資料庫 時,也應採用相同的判斷方式。
刻意選擇淘汰政策
全新的 Redis 預設為 noeviction。請檢查目前的設定:
redis-cli config get maxmemory-policy在 noeviction 下,執行個體滿載時會停止接受寫入,並回傳以下訊息:
(error) OOM command not allowed when used memory > 'maxmemory'.這是本指南中最嚴重的故障模式,因為網站不會停止運作,而是變慢。每次快取寫入都會失敗,因此 WordPress 會回到資料庫取得值,接著在下一個請求中再次嘗試儲存,但仍會失敗。此時,網站除了原本所有的資料庫作業外,每個 key 還要額外往返 Redis 一次。WordPress 管理介面不會告訴你發生了這件事。這段字串會出現在 PHP error log 中,因此網站在加入快取後變慢時,請針對 OOM command not allowed 執行 grep。
allkeys-lru 是這裡適當的預設值。記憶體不足時,Redis 會丟棄最近最少使用的 key。這正是物件快取需要的行為,因為其中每個值都是 MySQL 中仍然存在之資料的副本。遺失一個 key 只會增加一次查詢。拒絕一次寫入,則會讓每個請求中的所有查詢都增加負擔,直到有人發現問題為止。
此用途應避免使用 volatile-* 政策。這些政策只會考慮具有到期時間的 key;Redis 文件指出,當沒有任何 key 具備到期時間時,其行為會像 noeviction。WordPress 儲存的大多數物件快取項目都沒有 TTL,因此物件快取使用 volatile-lru 可能會填滿,並開始拒絕寫入。如果流量經常集中在少數幾個 key 上,allkeys-lfu 也是合理的替代方案,因為它會依存取頻率而非最近使用時間進行淘汰。請刻意選擇一項,並記錄選擇原因。
Persistence:除非有明確理由,否則關閉
套件提供的 redis.conf 會透過類似 save 900 1 的設定啟用 RDB snapshot,並關閉 append-only file。對純物件快取而言,snapshot 沒有實際效益。依定義,這些資料都能重新產生;而從 20 分鐘前的檔案還原的快取,會包含過期值,WordPress 仍會信任這些值。
Snapshot 也會消耗資源。BGSAVE 會 fork 程序;子程序寫入時,copy-on-write 可能使記憶體使用量大幅增加。在小型 VPS 上,這會出現在 Redis log 中:
Can't save in background: fork: Cannot allocate memory啟動時通常也會出現以下警告。這表示 Redis 判斷之後的 fork 可能會失敗:
WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.若要關閉 snapshot,請在 /etc/redis/redis.conf 中設定空的 save 排程,重新啟動服務,並確認該值恢復為空白。
save ""sudo systemctl restart redis-server
redis-cli config get save只有在同一個 instance 還保存無法重建的資料時,才保留 persistence,例如 job queue 或 rate-limit counter。此時請將兩者分開。快取需要淘汰 keys,而持久資料需要保留 keys;maxmemory 與 eviction 會套用至整個 instance,而不是單一 database index。使用兩個 socket 上的兩個 instance,才是最簡潔的做法。
安裝外掛並了解 drop-in
wp plugin install redis-cache --activate
wp redis enable
wp redis statuswp redis enable 成功時會輸出 Object cache enabled.。實際上,它會將 wp-content/plugins/redis-cache/includes/object-cache.php 複製到 wp-content/object-cache.php。該複本就是 drop-in,而 drop-in 才是實際執行工作的部分。WordPress 會在非常早期載入 wp-content/object-cache.php,早於任何外掛程式碼執行,因此整個請求都能使用快取。已啟用但沒有 drop-in 的外掛不會快取任何內容。
錯誤訊息會指出哪一部分發生故障。Object cache could not be enabled. 表示複製失敗,因此執行 WP-CLI 的使用者無法寫入 wp-content。A foreign object cache drop-in was found. 表示另一個快取外掛已經使用該檔名,解決方式是 wp redis update-dropin。如果訊息以 Redis server is unreachable: 結尾,接著又出現用戶端錯誤,表示連線設定不正確,請返回 redis-cli ping。
如果複製因權限問題失敗,請手動放置檔案,並將檔案擁有者設為 Web 使用者。
cp wp-content/plugins/redis-cache/includes/object-cache.php wp-content/object-cache.php
sudo chown www-data:www-data wp-content/object-cache.php移除外掛不會移除 drop-in。請先執行 wp redis disable;該指令會輸出 Object cache disabled. 並刪除檔案。如果在 drop-in 仍存在時刪除外掛目錄,網站會繼續執行舊的快取程式碼,但沒有外掛可更新該程式碼。
wp-config.php 中的連線設定
請將以下內容加在寫有 /* That's all, stop editing! */ 的那一行之前,因為在該行之後定義常數已經太晚。
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_PREFIX', 'shop_prod:' );使用 Unix socket 時,請設定 scheme 與路徑。此時會忽略主機與連接埠。
define( 'WP_REDIS_SCHEME', 'unix' );
define( 'WP_REDIS_PATH', '/run/redis/redis-server.sock' );
define( 'WP_REDIS_PREFIX', 'shop_prod:' );WP_REDIS_MAXTTL 會以秒為單位,強制所有 key 設定到期時間。使用 allkeys-lru 時不需要設定此項;如果您需要嚴格限制快取值可能過期多久,則可使用此項。
一個 Redis,多個網站:前綴與資料庫
Redis 預設提供 16 個編號資料庫,每個資料庫內都有獨立的扁平 keyspace。若兩個 WordPress 安裝都指向 database 0,且未設定前綴,便會將相同的 key 名稱寫入同一個空間。因此,其中一個網站可能讀取並提供另一個網站的選項資料。請為每個網站設定專屬前綴。
define( 'WP_REDIS_PREFIX', 'shopA_prod:' );
define( 'WP_REDIS_DATABASE', 1 );前綴可區分 key 名稱。資料庫索引則可區分 keyspace,這在清除資料時很重要:清空其中一個索引不會影響其他索引。該 plugin 也說明了 WP_REDIS_SELECTIVE_FLUSH;這只會刪除符合前綴的 key,而不是整個資料庫,但必須先掃描這些 key。
前綴與索引都無法區分記憶體。maxmemory 與 eviction policy 會套用到整個 instance,因此繁忙的網站可能將低流量網站的 key 淘汰,而兩者都不會回報這種情況。若網站之間不得互相影響,請使用分離的 Redis instance;每個 instance 都應有自己的 socket 與限制。
讓 staging 排除在 production 的快取之外
staging 網站通常是 production 檔案與資料庫的副本,也就是複製了帶有相同前綴與相同資料庫索引的 wp-config.php。如果將它指向相同的 Redis,staging 就會使用 staging 值寫入 production 的 key。測試價格或變更選項可能因此在未部署、也沒有追蹤紀錄的情況下出現在正式網站。
請手動為各環境設定不同的 salt。在 staging 的 wp-config.php 中:
define( 'WP_REDIS_PREFIX', 'shop_staging:' );
define( 'WP_REDIS_DATABASE', 5 );更好的做法是為 staging 配置專用的 Redis instance,或完全不使用 object cache。define( 'WP_REDIS_DISABLED', true ); 會在執行階段關閉快取,但保留 drop-in。這也是最快確認問題是否由快取造成的方法。
較舊的教學會為此設定 WP_CACHE_KEY_SALT。該 plugin 的 readme 將此 constant 標示為 deprecated,並指出已由 WP_REDIS_PREFIX 取代,因此請使用新的名稱。
驗證,不要只相信
先從外掛本身的診斷功能開始。
wp redis status最重要的是 Drop-in。Drop-in: Valid 表示 WordPress 正在載入此外掛的檔案。Drop-in: Not installed 表示複製從未完成,網站沒有持久性快取,即使管理介面看起來顯示正常也是如此。Status 回報連線狀態,Client 則指出目前使用的 extension。請在此確認使用的是 PhpRedis,而不是 Predis。
接著直接向 WordPress core 查詢,因為 core 不會受外掛判斷影響。
wp eval 'var_dump( wp_using_ext_object_cache() );'bool(true) 表示 core 正在使用外部 object cache。
接著使用你設定的 prefix,確認是否有 key 寫入。
redis-cli -n 0 dbsize
redis-cli -n 0 --scan --pattern 'shop_prod:*' | head你在網站上瀏覽時,dbsize 持續上升就是證明。若 drop-in 有效但 key 數量為 0,表示連線可能在無訊息的情況下失敗,或使用的 prefix 並不是你以為的那個值。
最後查看 Redis 所提供的統計資料。
redis-cli info stats | grep -E 'keyspace_hits|keyspace_misses|evicted_keys|expired_keys'命中率是 keyspace_hits / (keyspace_hits + keyspace_misses),Redis 的文件提供了這個公式。解讀時要注意兩點。這些計數器涵蓋該 instance 自上次重新啟動後的所有資料,因此會混合所有共用該 instance 的網站與應用程式。其次,flush 或重新啟動後立即計算的比率沒有意義,因為快取仍在填充。請讓它運作一個正常流量日。
不要將你的數值與 hosting company 公布的命中率或查詢數量比較。那些數據描述的是其網站與外掛組合。真正重要的是你自己的數值,並且要在無法由 page cache 提供服務的頁面上,分別於啟用前後進行測量。
curl -o /dev/null -s -w '%{time_starttransfer}\n' -b cookies.txt https://example.com/my-account/使用已登入的 cookie jar 執行數次,先關閉快取(WP_REDIS_DISABLED),再啟用快取。兩者的差異就是結果。
Redis 讓 WordPress 變慢的情況
錯誤政策造成的完整執行個體是最主要的問題,上文已說明:日誌中會出現 OOM command not allowed when used memory > 'maxmemory'.,而網站同時負擔資料庫與快取的費用。
Redis 位於另一台主機則是第二個問題。WordPress 在一次請求中會執行數百次物件快取呼叫。若一次請求執行 500 次呼叫,而每次往返需要 1 ms,等待時間就會達到半秒;使用本機 socket 則不會有這段額外等待。請將 Redis 保留在同一台伺服器,或放在延遲低於 1 ms 的私有網路中。
大量自動載入的 options 資料表是第三個問題,舊網站尤其常見。WordPress 會將所有自動載入的 options 以單一 key 快取,因此每次請求都會透過連線傳送數 MB 的資料。請加以測量:
wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024) AS kb FROM wp_options WHERE autoload IN ('yes','on','auto','auto-on');"WordPress 6.6 新增了 autoload 值,因此舊版只比對 'yes' 的查詢,在現代安裝環境中會低估數值。超過 1 MB 就表示應修正 options 資料表,而不是 Redis。
重新啟動會清空所有內容,因此在 systemctl restart redis-server 之後的幾分鐘內,所有請求都會發生快取未命中,並執行資料庫作業。請在流量較低時重新啟動。此外,物件快取不會阻止 wp-cron.php 在訪客載入頁面時執行;這本身也可能造成請求變慢。趁現在將 WP-Cron 移至真正的系統 cron 工作。
日常維護
部署變更選項或佈景主題程式碼後,使用 wp cache flush 清除快取。如果外掛程式更新後 drop-in 沒有自行更新,請執行 wp redis update-dropin。較舊外掛程式版本的 drop-in 搭配較新版本的外掛程式,確實可能造成異常行為。使用 redis-cli --stat 監看執行中的伺服器;它每秒輸出一行。redis-cli monitor 會列出每個命令,並在繁忙的 instance 上實際消耗 CPU。因此,請在重現問題時使用數秒,然後停止。
最後,有一個值得了解的數值:redis-cli info clients 會回報 connected_clients。PHP-FPM 每個 worker 都會保留一個連線,因此這個數值應接近 pm.max_children,不應高出一個數量級。如果超出,表示有程式正在建立連線但沒有關閉。
FAQ
如果使用 Redis 物件快取,是否仍需要頁面快取?
需要,匿名流量仍然需要。頁面快取會直接提供儲存的 HTML,不執行 PHP。這一律比搭配暖機物件快取執行 WordPress 的成本低。物件快取會處理頁面快取必須略過的請求:已登入使用者、購物車、結帳流程及 wp-admin。在購物網站或會員網站上,兩者都值得啟用。若網站訪客從不登入,幾乎所有工作都由頁面快取處理。
WordPress 應配置多少記憶體給 Redis?
請根據自己的伺服器計算,不要直接套用固定數值。從總 RAM 開始,扣除 MySQL buffer pool 與每個連線的 buffer,再扣除 pm.max_children 乘以單一 PHP-FPM worker 的常駐大小,並為 kernel 與 web server 保留數百 MB。將剩餘記憶體的一部分配置給 Redis,然後在流量運作一天後檢查 used_memory_human 於 redis-cli info memory 的數值,再視情況調整。單一 WordPress 網站通常只需數十 MB,因此在 4 GB 伺服器上,256 MB 的 maxmemory 是寬裕的起始值。
為什麼啟用 Redis 物件快取後,網站反而變慢?
最常見的原因是執行 noeviction policy 的 instance 已經滿載。Redis 會拒絕新的寫入並回傳 OOM command not allowed when used memory > 'maxmemory'.,因此 WordPress 會針對每個值退回資料庫查詢,並額外承擔一次無效的 Redis round trip。請檢查 redis-cli config get maxmemory-policy、設定 allkeys-lru,並確認 maxmemory 不要設得太小。其他常見原因包括 Redis server 位於遠端主機,導致每個請求累積數百次 round trip;以及 autoloaded options 的值達到數 MB,每次請求都必須透過連線傳送。
多個 WordPress 網站可以共用同一台 Redis server 嗎?
可以,但必須妥善設定。為每個網站指定唯一的 WP_REDIS_PREFIX,避免 key 名稱衝突;再使用不同的 WP_REDIS_DATABASE index,確保清除一個網站的快取時不會清空其他網站的資料。各網站仍會共用記憶體:maxmemory 與 eviction 會套用到整個 instance,因此流量大的網站可能淘汰流量小網站的 keys。若網站之間不得互相影響,應使用各自具有限制的獨立 Redis instances。
刪除 wp-content/object-cache.php 是否安全?
安全。這是 drop-in,不屬於 WordPress core。刪除後,WordPress 會恢復使用內建的每次請求快取。網站仍可正常運作,只是會執行更多資料庫查詢。建議使用 wp redis disable,該指令會乾淨地刪除檔案,並回報 Object cache disabled.。如果 Redis 已停止或運作異常,且無法進入管理介面,也可以手動刪除檔案,這是正確的緊急處理方式。