วิธีติดตั้ง Redis Object Cache บน WordPress VPS
เรียนรู้วิธีติดตั้ง Redis เพื่อทำ Object Cache บน WordPress บน VPS ของคุณเอง ตั้งค่าการเชื่อมต่อ localhost กำหนด maxmemory และเลือกนโยบาย eviction พร้อมวิธีตรวจสอบสถานะการทำงานจริง
Redis object cache มีประโยชน์อย่างไรต่อ WordPress
Redis object cache สำหรับ WordPress จะจัดเก็บผลลัพธ์ของการสืบค้นฐานข้อมูลไว้ในหน่วยความจำ เพื่อให้การร้องขอในครั้งถัดไปสามารถอ่านข้อมูลจาก Redis ได้โดยตรง แทนที่จะต้องสอบถามไปยัง MySQL อีกครั้ง โดยปกติแล้ว WordPress จะมี object cache พื้นฐานมาให้ในตัวคือ WP_Object_Cache แต่ข้อมูลดังกล่าวจะถูกเก็บไว้ในหน่วยความจำของ PHP และจะถูกลบทิ้งทันทีเมื่อการร้องขอสิ้นสุดลง การใช้ไฟล์ drop-in จะเข้ามาแทนที่ระบบเดิมด้วยระบบที่สื่อสารกับ Redis ทำให้ข้อมูลในแคชยังคงอยู่ต่อเนื่องจากการร้องขอหนึ่งไปยังอีกการร้องขอหนึ่ง
Object caching ไม่ใช่ page caching และความแตกต่างนี้เป็นตัวตัดสินว่าคู่มือนี้มีประโยชน์ต่อคุณหรือไม่ Page cache จะจัดเก็บ HTML ที่ประมวลผลเสร็จแล้วของ URL นั้นๆ และส่งคืนให้ผู้ใช้โดยไม่ต้องประมวลผล PHP เลย ซึ่งวิธีนี้เร็วกว่าสิ่งที่ Redis ทำได้ และเหมาะสำหรับผู้เยี่ยมชมที่ไม่ได้ล็อกอิน แต่ทันทีที่มีการล็อกอิน เพิ่มสินค้าลงในตะกร้า หรือเข้าใช้งานหน้า admin ระบบ page cache จะหยุดทำงานและ WordPress จะต้องประมวลผลการร้องขอทั้งหมดใหม่ ตั้งแต่ขั้นตอน bootstrap, การโหลดปลั๊กอิน ไปจนถึงการสืบค้นฐานข้อมูล ซึ่ง object cache จะช่วยให้การร้องขอ เหล่านั้น มีต้นทุนในการประมวลผลที่ต่ำลง จึงเป็นเครื่องมือสำหรับจัดการ traffic ที่ page cache เข้าไม่ถึง เช่น เซสชันของผู้ใช้ที่ล็อกอิน, ตะกร้าสินค้า, การชำระเงิน และ wp-admin ซึ่งสำหรับร้านค้า WooCommerce นี่คือส่วนของ traffic ที่ใช้ทรัพยากรสูงที่สุด
ทั้งสองระบบสามารถทำงานร่วมกันได้ และสำหรับเว็บไซต์ที่มีผู้ใช้งานจำนวนมากควรมีทั้งสองระบบ คุณต้องชัดเจนว่ากำลังแก้ไขปัญหาใดอยู่ เว็บไซต์ประชาสัมพันธ์ที่มีผู้อ่านทั่วไปจะได้รับความเร็วเกือบทั้งหมดจาก page cache และการเพิ่ม Redis เข้าไปอาจไม่ได้ช่วยให้เห็นความแตกต่างมากนัก
ข้อจำกัดที่ต้องทราบก่อนเริ่มใช้งานคือ object cache ไม่ได้ทำให้การสืบค้นที่ช้ากลายเป็นเร็ว แต่เป็นการลดการสืบค้น ซ้ำ ของคำสั่งที่เคยทำงานไปแล้ว การร้องขอครั้งแรกหลังจากที่ข้อมูลไม่อยู่ในแคช (cache miss) จะต้องใช้ทรัพยากรเต็มจำนวน ดังนั้นหากปลั๊กอินมีการสืบค้นที่ไม่ได้ทำ index ไว้ มันก็จะยังคงทำงานหนึ่งครั้งต่อรอบอายุของแคชนั้นๆ
สิ่งที่ต้องเตรียมก่อนเริ่ม
- Linux VPS ที่เข้าถึงผ่าน shell ได้และมี
sudoไม่จำเป็นต้องใช้ control panel - WordPress ที่ทำงานผ่าน PHP-FPM เช่น บน LAMP stack บน Ubuntu 24.04
- WP-CLI บนเซิร์ฟเวอร์ ทุกขั้นตอนในที่นี้มีเมนูเทียบเท่าในหน้า admin แต่การใช้ shell จะรวดเร็วกว่า
- Redis บนเครื่องเดียวกับ PHP เพราะจุดประสงค์หลักคือการลด latency และการส่งข้อมูลผ่านเครือข่ายจะทำให้ประสิทธิภาพลดลง
คำสั่งด้านล่างนี้เขียนขึ้นสำหรับ Ubuntu 24.04 ที่ใช้ PHP 8.3 และผู้ใช้งานเว็บคือ www-data โปรดปรับเปลี่ยนเวอร์ชัน PHP และชื่อผู้ใช้งานให้ตรงกับเซิร์ฟเวอร์ของคุณ ให้รันคำสั่ง wp จากไดเรกทอรีของ WordPress ซึ่งเป็นที่ตั้งของไฟล์ 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 ซึ่งเป็นส่วนขยายภาษา C จาก PECL โดยมีความเร็วสูงกว่า Predis ที่เขียนด้วย PHP ล้วน และปลั๊กอินจะเรียกใช้งานส่วนขยายนี้โดยอัตโนมัติหากตรวจพบว่ามีการติดตั้งไว้ PHP-FPM จะโหลดส่วนขยายเมื่อเริ่มต้นทำงาน ดังนั้นส่วนขยายใหม่จะไม่ปรากฏจนกว่าคุณจะรีสตาร์ท pool
sudo systemctl restart php8.3-fpm
php -m | grep redisโปรดระมัดระวังในการตรวจสอบขั้นตอนสุดท้าย: php -m จะแสดงรายการโมดูลของ PHP ที่ทำงานผ่านบรรทัดคำสั่ง (command line) แต่ FPM อาจโหลดชุดโมดูลที่แตกต่างกัน การตรวจสอบที่เชื่อถือได้ที่สุดคือการดูจากระบบวินิจฉัยของตัวปลั๊กอินเองในขั้นตอนถัดไป
ณ เดือนสิงหาคม 2026 Ubuntu 24.04 มีแพ็กเกจ Redis 7.0.15 ซึ่งเพียงพอสำหรับการใช้งานเป็น object cache หากคุณต้องการเวอร์ชันล่าสุด 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หาก Linux distribution ของคุณมาพร้อมกับ Valkey ซึ่งเป็น fork ที่เกิดขึ้นหลังจากการเปลี่ยนสัญญาอนุญาตในปี 2024 ระบบจะใช้โปรโตคอลเดียวกัน และคำแนะนำทั้งหมดด้านล่างนี้สามารถนำไปใช้ได้โดยไม่ต้องปรับเปลี่ยนใดๆ
การจำกัดการเข้าถึง Redis เพื่อความปลอดภัย
โดยค่าเริ่มต้น Redis จะไม่มีการตั้งรหัสผ่าน อุปกรณ์ใดก็ตามที่สามารถเชื่อมต่อพอร์ต 6379 ได้จะสามารถอ่านค่าที่แคชไว้ทั้งหมดและรันคำสั่ง FLUSHALL ได้ อินสแตนซ์ที่เปิดเผยต่ออินเทอร์เน็ตจะถูกสแกนพบภายในเวลาไม่กี่ชั่วโมง ดังนั้นการตั้งค่าเครือข่ายจึงมีความสำคัญก่อนการปรับแต่งประสิทธิภาพ
เปิดไฟล์ /etc/redis/redis.conf และตรวจสอบให้แน่ใจว่ามีบรรทัดเหล่านี้:
bind 127.0.0.1 -::1
protected-mode yesจากนั้นตรวจสอบว่ามีการใช้งานพอร์ตจริงหรือไม่ เนื่องจากไฟล์คอนฟิกเป็นเพียงการตั้งค่าที่ระบุไว้ แต่ ss คือหลักฐานยืนยันการทำงานจริง
sudo ss -lntp | grep 6379ค่าที่ต้องการคือ 127.0.0.1:6379 หากพบ 0.0.0.0:6379 หมายความว่า Redis กำลังตอบรับการเชื่อมต่อผ่านอินเทอร์เฟซสาธารณะ ให้แก้ไขบรรทัด bind แล้วรีสตาร์ทบริการ
ในกรณีที่ PHP และ Redis อยู่บนเซิร์ฟเวอร์เดียวกัน การใช้ Unix socket จะมีประสิทธิภาพดีกว่าการใช้ loopback TCP เนื่องจากไม่มีการประมวลผลผ่าน TCP stack และการเข้าถึงจะถูกควบคุมด้วยสิทธิ์ของไฟล์แทนการใช้กฎ firewall ซึ่งอาจมีการเปลี่ยนแปลงในภายหลัง
unixsocket /run/redis/redis-server.sock
unixsocketperm 770ไฟล์ socket นี้จะมีเจ้าของเป็นผู้ใช้และกลุ่ม redis ดังนั้นผู้ใช้เว็บเซิร์ฟเวอร์จะต้องถูกเพิ่มเข้าในกลุ่มดังกล่าว
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 pingคำสั่งนี้จะต้องแสดงผลเป็น PONG หากแสดงเป็น Could not connect to Redis at /run/redis/redis-server.sock: Permission denied หมายความว่าการเพิ่มกลุ่มยังไม่มีผล ให้ตรวจสอบไฟล์ id www-data และโปรดจำไว้ว่ากระบวนการ PHP-FPM ที่กำลังทำงานอยู่จะคงสิทธิ์ของกลุ่มเดิมที่ได้รับตอนเริ่มต้นไว้ จึงจำเป็นต้องรีสตาร์ทบริการตามรายการข้างต้น ทั้งนี้ควรเปิดใช้งาน TCP ทิ้งไว้จนกว่าจะยืนยันได้ว่าการเชื่อมต่อผ่าน socket ทำงานได้ปกติ เพื่อป้องกันไม่ให้การพิมพ์ผิดพลาดทำให้สูญเสียช่องทางการเข้าถึงทั้งสองทางพร้อมกัน
Redis ควรใช้หน่วยความจำเท่าใด
ให้คำนวณตัวเลขจากเซิร์ฟเวอร์ของคุณเอง Redis ที่ไม่มี maxmemory จะขยายขนาดไปเรื่อยๆ จนกว่า kernel จะไม่มีหน่วยความจำเหลือและ OOM killer จะเข้ามาหยุดการทำงานของโพรเซส ซึ่งมักจะเป็นโพรเซสที่ใช้หน่วยความจำมากที่สุด ในเซิร์ฟเวอร์ WordPress มักจะเป็น MySQL journalctl -k | grep -i "out of memory" จะแสดงการหยุดการทำงานดังกล่าวหลังจากที่เกิดขึ้นแล้ว ซึ่งในตอนนั้นเว็บไซต์จะล่มไปแล้ว
ให้เริ่มจาก RAM ทั้งหมดแล้วหักลบส่วนที่จำเป็นออก MySQL หรือ MariaDB จะจอง innodb_buffer_pool_size บวกกับ buffer ต่อการเชื่อมต่อ PHP-FPM จะใช้ต้นทุนเท่ากับ pm.max_children คูณด้วยขนาดหน่วยความจำจริงที่ worker หนึ่งตัวใช้ ซึ่งโดยทั่วไปอยู่ที่ 64 MB ถึง 128 MB สำหรับเว็บไซต์ที่ติดตั้งปลั๊กอินจำนวนมาก Kernel และเว็บเซิร์ฟเวอร์ต้องการหน่วยความจำอีกสองสามร้อยเมกะไบต์ ส่วนที่เหลือคือเพดานของคุณ และ Redis จะได้รับส่วนแบ่งจากตรงนั้น
ตัวอย่างการจัดสรรงบประมาณสำหรับ VPS ขนาด 4 GB ที่รันร้านค้าหนึ่งแห่ง
ตัวเลขเหล่านี้เป็นเพียงตัวอย่าง ไม่ใช่ค่าที่วัดได้จากเซิร์ฟเวอร์ของคุณ ให้แทนที่แต่ละค่าด้วยค่าที่เซิร์ฟเวอร์ของคุณรายงานจริง
- MariaDB พร้อม buffer pool ขนาด 1 GB: 1024 MB
- PHP-FPM, 10 workers ที่ 96 MB ต่อตัว: 960 MB
- Kernel, nginx หรือ Apache, sshd, logging: 512 MB
- ส่วนที่เหลือ: ประมาณ 1.5 GB
การตั้งค่า maxmemory ไว้ที่ 256 MB เป็นจุดเริ่มต้นที่สมเหตุสมผล ซึ่งจะเหลือพื้นที่ว่างไว้จริง และเว็บไซต์ WordPress เดี่ยวๆ มักไม่ต้องการมากกว่านี้
ตอนนี้ให้วัดค่าแทนการคาดเดา หลังจากผ่านไปหนึ่งวันที่มี traffic จริง:
redis-cli info memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'
redis-cli dbsizeหาก used_memory_human อยู่ต่ำกว่าขีดจำกัดของคุณมาก ให้ลดขีดจำกัดลงแล้วคืน RAM ให้กับ 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 แล้วอ่านค่ากลับมา การมีกำแพงชั้นที่สองนั้นคุ้มค่า: การจำกัด MemoryMax บน systemd unit จะช่วยป้องกันไม่ให้ Redis ที่ตั้งค่าผิดพลาดทำให้เซิร์ฟเวอร์ล่ม ให้ตั้งค่าไว้สูงกว่า maxmemory เสมอ ห้ามตั้งค่าให้เท่ากัน เพราะขีดจำกัดของ cgroup จะหยุดการทำงานของโพรเซสแทนที่จะลบ key ออก หาก Redis รันอยู่ในคอนเทนเนอร์ข้างๆ WordPress ควรใส่ตัวเลขเดียวกันใน ขีดจำกัดหน่วยความจำในไฟล์ Compose ของคุณ และเหตุผลเดียวกันนี้เป็นตัวกำหนดทางเลือกในการ รันฐานข้อมูลใน Docker หรือบนโฮสต์
เลือกนโยบายการไล่ข้อมูล (eviction policy) อย่างตั้งใจ
Redis ที่ติดตั้งใหม่จะมีค่าเริ่มต้นเป็น noeviction ให้ตรวจสอบการตั้งค่าของคุณด้วยคำสั่ง:
redis-cli config get maxmemory-policyภายใต้ noeviction อินสแตนซ์ที่เต็มจะไม่ยอมรับการเขียนข้อมูลและจะตอบกลับด้วยข้อความนี้:
(error) OOM command not allowed when used memory > 'maxmemory'.บรรทัดเดียวนี้คือรูปแบบความล้มเหลวที่เลวร้ายที่สุดในคู่มือนี้ เพราะเว็บไซต์ไม่ได้ล่ม แต่จะทำงานช้าลง การเขียนแคชทุกครั้งจะล้มเหลว ทำให้ WordPress ต้องกลับไปดึงข้อมูลจากฐานข้อมูลแทน จากนั้นจึงพยายามบันทึกข้อมูลลงแคชอีกครั้งในคำขอถัดไปและล้มเหลวซ้ำเดิม เว็บไซต์จะต้องแบกรับภาระงานฐานข้อมูลเดิมทั้งหมด บวกกับ การรับส่งข้อมูลไปยัง Redis สำหรับทุกคีย์ ไม่มีส่วนใดในหน้าผู้ดูแลระบบของ WordPress ที่จะแจ้งให้คุณทราบว่าเหตุการณ์นี้กำลังเกิดขึ้น ข้อความนี้จะปรากฏใน log ของ PHP ดังนั้นให้ใช้ grep ค้นหา OOM command not allowed เมื่อเว็บไซต์ทำงานช้าลงหลังจากที่คุณเพิ่มแคชเข้าไป
allkeys-lru คือค่าเริ่มต้นที่เหมาะสมสำหรับงานนี้ Redis จะลบเฉพาะคีย์ที่ไม่ได้ถูกใช้งานนานที่สุด (Least Recently Used) เมื่อหน่วยความจำเต็ม ซึ่งเป็นสิ่งที่ object cache ต้องการพอดี เพราะทุกค่าในแคชเป็นเพียงสำเนาของข้อมูลที่มีอยู่ใน MySQL อยู่แล้ว การสูญเสียคีย์หนึ่งคีย์มีต้นทุนเพียงแค่การคิวรีฐานข้อมูลหนึ่งครั้ง แต่การปฏิเสธการเขียนข้อมูลมีต้นทุนเท่ากับการคิวรีทุกครั้งในทุกคำขอ จนกว่าจะมีคนสังเกตเห็นปัญหา
หลีกเลี่ยงนโยบายกลุ่ม volatile-* สำหรับงานนี้ เนื่องจากนโยบายเหล่านี้จะพิจารณาเฉพาะคีย์ที่มีการกำหนดวันหมดอายุเท่านั้น และเอกสารของ Redis ระบุว่านโยบายเหล่านี้จะทำงานเหมือน noeviction หากไม่มีคีย์ใดมีวันหมดอายุเลย WordPress มักจัดเก็บรายการ object cache ส่วนใหญ่โดยไม่มี TTL ดังนั้นการใช้ volatile-lru กับ object cache อาจทำให้หน่วยความจำเต็มและเริ่มปฏิเสธการเขียนข้อมูลได้ allkeys-lfu เป็นทางเลือกที่ใช้ได้ดีหากทราฟฟิกของคุณเข้าถึงคีย์ชุดเล็กๆ บ่อยครั้ง เนื่องจากจะใช้วิธีไล่ข้อมูลตามความถี่แทนที่จะเป็นความใหม่ ให้เลือกนโยบายอย่างตั้งใจและจดบันทึกเหตุผลประกอบไว้ด้วย
Persistence: ปิดไว้เว้นแต่คุณจะมีเหตุผลจำเป็น
แพ็กเกจ redis.conf จะเปิดใช้งาน RDB snapshots ด้วยบรรทัดคำสั่งอย่าง save 900 1 และปิดการใช้งาน append-only file ไว้ สำหรับการใช้งานเป็น object cache เพียงอย่างเดียว snapshots ไม่ได้ให้ประโยชน์ใดๆ ข้อมูลสามารถสร้างใหม่ได้โดยนิยามอยู่แล้ว และ cache ที่ถูกกู้คืนมาจากไฟล์ที่มีอายุยี่สิบนาทีคือชุดข้อมูลที่ล้าสมัยซึ่ง WordPress จะยังคงเชื่อถือ
Snapshots ยังมีต้นทุนในการทำงาน BGSAVE จะทำการ fork กระบวนการ และกลไก copy-on-write หมายความว่าการใช้หน่วยความจำอาจพุ่งสูงขึ้นอย่างรวดเร็วในขณะที่ child process กำลังเขียนข้อมูล บน VPS ขนาดเล็ก สิ่งนี้จะปรากฏใน log ของ Redis ดังนี้:
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.หากต้องการปิด snapshots ให้ตั้งค่าตารางเวลา save เป็นค่าว่างใน /etc/redis/redis.conf จากนั้น restart และตรวจสอบว่าค่าที่ได้กลับมานั้นว่างเปล่า:
save ""sudo systemctl restart redis-server
redis-cli config get saveให้คงการทำ persistence ไว้เฉพาะในกรณีที่ instance เดียวกันนั้นเก็บข้อมูลที่คุณไม่สามารถสร้างใหม่ได้ เช่น คิวงาน (job queue) หรือตัวนับ rate-limit ในกรณีดังกล่าว ให้แยกทั้งสองส่วนออกจากกัน cache ต้องการให้มีการลบ keys ออก (eviction) แต่ข้อมูลที่ต้องการความคงทนต้องการให้เก็บ keys ไว้ และ maxmemory รวมถึงการตั้งค่า eviction จะมีผลกับทั้ง instance ไม่ใช่แค่ database index ใด index หนึ่ง การรันสอง instance บนสอง socket คือคำตอบที่สะอาดและเหมาะสมที่สุด
ติดตั้งปลั๊กอินและทำความเข้าใจเกี่ยวกับ 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 ซึ่งเป็นส่วนที่ทำหน้าที่ประมวลผล WordPress จะโหลด wp-content/object-cache.php ตั้งแต่ช่วงเริ่มต้นก่อนที่โค้ดปลั๊กอินใดๆ จะทำงาน นี่คือเหตุผลที่แคชพร้อมใช้งานตลอดทั้ง request หากปลั๊กอินถูกเปิดใช้งานแต่ไม่มีไฟล์ 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: ตามด้วยข้อผิดพลาดจากฝั่ง client แสดงว่าการตั้งค่าการเชื่อมต่อไม่ถูกต้อง ให้ย้อนกลับไปตรวจสอบที่ redis-cli ping
หากการคัดลอกไฟล์ล้มเหลวเนื่องจากปัญหาเรื่องสิทธิ์การเข้าถึง ให้คัดลอกไฟล์ด้วยตนเองและกำหนดสิทธิ์ให้เป็นของผู้ใช้งานเว็บเซิร์ฟเวอร์
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 และ path โดยระบบจะเพิกเฉยต่อค่า host และ port
define( 'WP_REDIS_SCHEME', 'unix' );
define( 'WP_REDIS_PATH', '/run/redis/redis-server.sock' );
define( 'WP_REDIS_PREFIX', 'shop_prod:' );WP_REDIS_MAXTTL จะบังคับให้คีย์ทุกตัวมีวันหมดอายุโดยระบุเป็นวินาที คุณไม่จำเป็นต้องใช้ค่านี้หากใช้ allkeys-lru ร่วมด้วย โดยค่านี้จะมีประโยชน์ในกรณีที่คุณต้องการกำหนดขีดจำกัดสูงสุดที่แน่นอนว่าค่าที่แคชไว้นั้นจะเก่าได้มากที่สุดเท่าใด
การใช้ Redis หนึ่งอินสแตนซ์กับหลายเว็บไซต์: การกำหนด prefix และ database
โดยค่าเริ่มต้น Redis จะมี database ให้ใช้งาน 16 หมายเลข และมี keyspace แบบแบนภายในแต่ละ database หากติดตั้ง WordPress สองชุดโดยชี้ไปที่ database 0 และไม่ได้กำหนด prefix ทั้งสองจะเขียนคีย์ชื่อเดียวกันลงในพื้นที่เดียวกัน ทำให้เว็บไซต์หนึ่งสามารถอ่านค่า options ของอีกเว็บไซต์หนึ่งและนำไปแสดงผลได้ ดังนั้นควรตั้งค่า prefix แยกกันสำหรับแต่ละเว็บไซต์
define( 'WP_REDIS_PREFIX', 'shopA_prod:' );
define( 'WP_REDIS_DATABASE', 1 );การใช้ prefix จะช่วยแยกชื่อคีย์ออกจากกัน ส่วนการใช้ index ของ database จะช่วยแยก keyspace ซึ่งมีความสำคัญในกรณีที่ต้องการล้างข้อมูล (flush) เพราะการล้างข้อมูลใน index หนึ่งจะไม่ส่งผลกระทบต่อ index อื่น ปลั๊กอินยังได้ระบุถึง WP_REDIS_SELECTIVE_FLUSH ซึ่งจะลบเฉพาะคีย์ที่ตรงกับ prefix ของคุณแทนการลบทั้ง database แต่จะมีค่าใช้จ่ายด้านประสิทธิภาพจากการที่ต้องสแกนหาคีย์เหล่านั้น
สิ่งที่ prefix และ index ไม่สามารถแยกออกจากกันได้คือหน่วยความจำ การตั้งค่า maxmemory และนโยบายการไล่ข้อมูล (eviction policy) จะมีผลกับทั้งอินสแตนซ์ ดังนั้นเว็บไซต์ที่มีการใช้งานสูงอาจทำให้คีย์ของเว็บไซต์ที่มีการใช้งานน้อยถูกลบออกไปโดยที่ไม่มีเว็บไซต์ใดแจ้งเตือน หากเว็บไซต์เหล่านั้นไม่ต้องการให้ส่งผลกระทบต่อกัน จำเป็นต้องแยก Redis ออกเป็นคนละอินสแตนซ์ โดยแต่ละอินสแตนซ์ต้องมี socket และขีดจำกัดหน่วยความจำของตัวเอง
แยกแคชของ staging ออกจาก production
โดยปกติแล้ว staging site คือสำเนาของไฟล์และฐานข้อมูลจาก production ซึ่งหมายความว่าเป็นสำเนาของ wp-config.php ที่ใช้ prefix และดัชนีฐานข้อมูลเดียวกัน หากชี้ไปที่ Redis ตัวเดียวกัน staging จะเขียนทับ key ของ production ด้วยค่าจาก staging ส่งผลให้ราคาที่ใช้ทดสอบหรือตัวเลือกที่ถูกเปลี่ยนไปปรากฏบนเว็บไซต์จริงโดยไม่มีการ deploy และไม่ทิ้งร่องรอยไว้
ให้กำหนดค่า salt แยกแต่ละสภาพแวดล้อมด้วยตนเอง ในไฟล์ wp-config.php ของ staging:
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 สำหรับกรณีนี้ แต่ไฟล์ readme ของปลั๊กอินระบุว่าค่าคงที่ดังกล่าวถูกเลิกใช้งานแล้วและถูกแทนที่ด้วย WP_REDIS_PREFIX ดังนั้นควรใช้ชื่อใหม่นี้แทน
ตรวจสอบให้แน่ใจแทนการเชื่อถือเพียงอย่างเดียว
เริ่มต้นด้วยการวินิจฉัยจากตัวปลั๊กอินเอง
wp redis statusบรรทัดที่มีความสำคัญที่สุดคือ Drop-in หากขึ้นว่า Drop-in: Valid หมายความว่า WordPress กำลังโหลดไฟล์ของปลั๊กอินนี้อยู่ แต่ถ้าขึ้นว่า Drop-in: Not installed หมายความว่าการคัดลอกไฟล์ไม่สำเร็จและเว็บไซต์ไม่มี persistent cache แม้ว่าหน้าจอผู้ดูแลระบบจะแสดงสถานะเป็นสีเขียวก็ตาม ส่วน Status จะรายงานสถานะการเชื่อมต่อ และ Client จะระบุชื่อ extension ที่ใช้งานอยู่ ซึ่งเป็นจุดที่คุณต้องตรวจสอบให้แน่ใจว่าเป็น PhpRedis ไม่ใช่ Predis
จากนั้นให้สอบถามไปยัง WordPress core โดยตรง เพราะ core ไม่ได้สนใจว่าปลั๊กอินจะรายงานอย่างไร
wp eval 'var_dump( wp_using_ext_object_cache() );'หากขึ้นว่า bool(true) หมายความว่า core กำลังสื่อสารกับ external object cache อยู่
จากนั้นพิสูจน์ว่ามี key เข้ามาจริงหรือไม่ โดยใช้ prefix ที่คุณกำหนดไว้
redis-cli -n 0 dbsize
redis-cli -n 0 --scan --pattern 'shop_prod:*' | headค่า dbsize ที่เพิ่มขึ้นในขณะที่คุณคลิกใช้งานเว็บไซต์คือหลักฐานยืนยัน หากจำนวน key เป็นศูนย์ทั้งที่มี drop-in ที่ถูกต้อง แสดงว่าการเชื่อมต่อกำลังล้มเหลวโดยไม่มีการแจ้งเตือน หรือ prefix ที่ใช้อาจไม่ตรงกับที่คุณเข้าใจ
สุดท้าย ให้ดูสิ่งที่ Redis วัดผลให้คุณ
redis-cli info stats | grep -E 'keyspace_hits|keyspace_misses|evicted_keys|expired_keys'อัตรา hit ratio คือ keyspace_hits / (keyspace_hits + keyspace_misses) ซึ่งเอกสารของ Redis ได้ให้สูตรคำนวณไว้ โดยมีข้อควรระวังสองประการ ประการแรก ตัวนับเหล่านี้ครอบคลุมทั้ง instance ตั้งแต่การรีสตาร์ทครั้งล่าสุด ดังนั้นจึงเป็นการรวมข้อมูลของทุกเว็บไซต์และทุกแอปพลิเคชันที่ใช้งานร่วมกัน ประการที่สอง อัตราส่วนที่ได้ทันทีหลังจาก flush หรือรีสตาร์ทไม่มีความหมาย เนื่องจาก cache กำลังอยู่ในระหว่างการเติมข้อมูล ควรปล่อยให้ระบบทำงานผ่านช่วงที่มี traffic ปกติไปก่อน
อย่าเปรียบเทียบตัวเลขของคุณกับ hit rate หรือจำนวน query ที่บริษัทโฮสติ้งเผยแพร่ เพราะตัวเลขเหล่านั้นอ้างอิงจากเว็บไซต์และชุดปลั๊กอินของพวกเขา ตัวเลขที่สำคัญคือตัวเลขของคุณเอง ซึ่งวัดผลก่อนและหลังการเปิดใช้งานบนหน้าที่ page cache ไม่สามารถให้บริการได้
curl -o /dev/null -s -w '%{time_starttransfer}\n' -b cookies.txt https://example.com/my-account/ให้รันคำสั่งดังกล่าวโดยใช้ cookie jar ของผู้ใช้ที่ล็อกอินอยู่ ทำซ้ำหลายๆ ครั้ง โดยปิด cache ไว้ก่อน (WP_REDIS_DISABLED) แล้วจึงเปิดใช้งาน ผลต่างที่ได้คือผลลัพธ์ของคุณ
เมื่อ Redis ทำให้ WordPress ทำงานช้าลง
การใช้ instance เต็มรูปแบบด้วยนโยบายที่ไม่เหมาะสมเป็นสาเหตุหลัก ซึ่งได้กล่าวถึงไปแล้วข้างต้น โดยสังเกตได้จาก OOM command not allowed when used memory > 'maxmemory'. ใน log ซึ่งส่งผลให้เว็บไซต์ต้องแบกรับภาระทั้งจากฐานข้อมูลและแคช
สาเหตุที่สองคือการวาง Redis ไว้บนโฮสต์อื่น WordPress มีการเรียกใช้งาน object cache หลายร้อยครั้งในหนึ่ง request หากหนึ่ง request มีการเรียก 500 ครั้งและแต่ละครั้งใช้เวลาไป-กลับ 1 ms จะทำให้เกิดการรอคอยรวมครึ่งวินาที ซึ่งจะไม่เกิดขึ้นหากใช้ local socket ควรเก็บ Redis ไว้บนเครื่องเดียวกัน หรือบนเครือข่ายส่วนตัวที่มีค่า latency ต่ำกว่าระดับมิลลิวินาที ส่วนกรณีที่การรันบนเครื่องเดียวกันได้รับประโยชน์จาก kernel มากน้อยเพียงใดนั้นเป็นอีกประเด็นหนึ่ง โดย cache aware scheduling ที่เพิ่มเข้ามาใน Linux 7.2 พยายามจัดให้กระบวนการที่มีการสื่อสารกันบ่อยอย่าง PHP-FPM และ Redis ทำงานบน core ที่แชร์แคชร่วมกัน ซึ่ง VPS guest จะได้รับประโยชน์ในส่วนนี้น้อยกว่าการรันบน bare metal
สาเหตุที่สามคือตาราง options ที่มีการ autoload ขนาดใหญ่ ซึ่งพบได้บ่อยในเว็บไซต์เก่า WordPress จะแคช options ที่ตั้งค่าให้ autoload ทั้งหมดไว้เป็น key เดียว ดังนั้นข้อมูลขนาดหนึ่งเมกะไบต์จะถูกส่งผ่านการเชื่อมต่อในทุกๆ request ให้ตรวจสอบด้วยคำสั่งนี้:
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 ใหม่ๆ เข้ามา ดังนั้น query แบบเก่าที่ค้นหาเพียง 'yes' จึงแสดงผลน้อยกว่าความเป็นจริงในการติดตั้งเวอร์ชันปัจจุบัน หากขนาดข้อมูลเกินหนึ่งเมกะไบต์ คุณควรแก้ไขที่ตาราง options ไม่ใช่ที่ Redis
การ restart จะทำให้ข้อมูลทั้งหมดหายไป ดังนั้นช่วงเวลาไม่กี่นาทีหลังจาก systemctl restart redis-server จะมีแต่ cache miss และภาระงานทั้งหมดจะตกไปอยู่ที่ฐานข้อมูล ควร restart ในช่วงที่มี traffic ต่ำ นอกจากนี้ object cache ไม่ได้ช่วยหยุดการทำงานของ wp-cron.php เมื่อมีผู้เข้าชมหน้าเว็บ ซึ่งเป็นสาเหตุหนึ่งที่ทำให้ request ทำงานช้าลง คุณควร ย้าย WP-Cron ไปใช้ system cron job จริง ในระหว่างที่คุณดำเนินการในส่วนนี้
การดูแลรักษาทั่วไป
ให้ล้างแคชหลังการปรับใช้ (deploy) ที่มีการเปลี่ยนแปลงตัวเลือกหรือโค้ดธีมด้วย wp cache flush หาก drop-in ไม่ได้อัปเดตตัวเองหลังจากการอัปเดตปลั๊กอิน ให้รัน wp redis update-dropin เนื่องจาก drop-in จากปลั๊กอินเวอร์ชันเก่าที่ทำงานร่วมกับปลั๊กอินเวอร์ชันใหม่เป็นสาเหตุหลักของพฤติกรรมที่ผิดปกติ ให้ตรวจสอบเซิร์ฟเวอร์แบบเรียลไทม์ด้วย redis-cli --stat ซึ่งจะแสดงผลหนึ่งบรรทัดต่อวินาที ส่วน redis-cli monitor จะแสดงทุกคำสั่งที่ทำงานและใช้ CPU จริงบนอินสแตนซ์ที่มีภาระงานสูง ดังนั้นให้ใช้เพียงไม่กี่วินาทีในขณะที่คุณกำลังจำลองปัญหา แล้วจึงหยุดการทำงาน
ตัวเลขสุดท้ายที่ควรทราบคือ redis-cli info clients ซึ่งจะรายงาน connected_clients โดยปกติ PHP-FPM จะคงการเชื่อมต่อไว้หนึ่งรายการต่อหนึ่ง worker ดังนั้นตัวเลขดังกล่าวควรสอดคล้องกับ pm.max_children ของคุณ และไม่ควรสูงเกินกว่านั้นหลายเท่า หากตัวเลขสูงเกินไป แสดงว่ามีบางอย่างกำลังเปิดการเชื่อมต่อค้างไว้และไม่ได้ปิดการเชื่อมต่อเหล่านั้น
FAQ
ฉันยังจำเป็นต้องใช้ page cache หากฉันรัน Redis object cache อยู่หรือไม่?
จำเป็น สำหรับทราฟฟิกของผู้ใช้ทั่วไป (anonymous traffic) page cache จะทำหน้าที่เสิร์ฟ HTML ที่เก็บไว้โดยไม่ต้องรัน PHP ซึ่งประหยัดทรัพยากรกว่าการรัน WordPress แม้จะมี object cache ที่พร้อมใช้งานแล้วก็ตาม object cache จะเข้ามาจัดการคำขอที่ page cache ไม่สามารถทำได้ เช่น ผู้ใช้ที่ล็อกอินอยู่, ตะกร้าสินค้า, หน้าชำระเงิน และ wp-admin สำหรับเว็บไซต์ร้านค้าหรือเว็บไซต์สมาชิก การใช้งานทั้งสองอย่างควบคู่กันนั้นคุ้มค่า แต่สำหรับเว็บไซต์ที่ผู้เข้าชมไม่เคยล็อกอิน page cache จะเป็นส่วนที่รับภาระงานเกือบทั้งหมด
ฉันควรจัดสรรหน่วยความจำให้ Redis สำหรับ WordPress เท่าใด?
ควรคำนวณจากทรัพยากรบนเซิร์ฟเวอร์ของคุณเองแทนการคัดลอกตัวเลขจากที่อื่น ให้เริ่มจาก RAM ทั้งหมด ลบด้วย MySQL buffer pool และ buffer ต่อการเชื่อมต่อแต่ละรายการ ลบด้วย pm.max_children คูณกับขนาดหน่วยความจำที่ PHP-FPM worker หนึ่งตัวใช้จริง และลบออกอีกสองสามร้อยเมกะไบต์สำหรับ kernel และเว็บเซิร์ฟเวอร์ จากนั้นจัดสรรส่วนที่เหลือบางส่วนให้ Redis แล้วตรวจสอบ used_memory_human ใน redis-cli info memory หลังจากผ่านไปหนึ่งวันที่มีทราฟฟิกเข้ามาแล้วจึงค่อยปรับเปลี่ยน โดยปกติเว็บไซต์ WordPress เดี่ยวจะใช้หน่วยความจำเพียงหลักสิบเมกะไบต์ ดังนั้นการตั้งค่า maxmemory ไว้ที่ 256 MB จึงถือเป็นการเริ่มต้นที่เพียงพอสำหรับเซิร์ฟเวอร์ขนาด 4 GB
ทำไมเว็บไซต์ของฉันถึงช้าลงหลังจากเปิดใช้งาน Redis object cache?
สาเหตุที่พบบ่อยคือ instance เต็มและกำลังรันนโยบาย noeviction อยู่ Redis จะปฏิเสธการเขียนข้อมูลใหม่และส่งค่า OOM command not allowed when used memory > 'maxmemory'. กลับมา ทำให้ WordPress ต้องกลับไปดึงข้อมูลจากฐานข้อมูลสำหรับทุกค่า และยังต้องเสียเวลาในการเชื่อมต่อกับ Redis โดยเปล่าประโยชน์อีกด้วย ให้ตรวจสอบ redis-cli config get maxmemory-policy, ตั้งค่า allkeys-lru และยืนยันว่า maxmemory ไม่ได้มีขนาดเล็กจนเกินไป สาเหตุอื่นที่พบบ่อยคือการวาง Redis server ไว้บนโฮสต์ระยะไกล ซึ่งทำให้เกิดการรับส่งข้อมูลหลายร้อยครั้งต่อหนึ่งคำขอจนสะสมเป็นความล่าช้า และการมีค่า options ที่โหลดอัตโนมัติขนาดหลายเมกะไบต์ซึ่งต้องส่งผ่านการเชื่อมต่อในทุกคำขอ
เว็บไซต์ WordPress หลายแห่งสามารถใช้ Redis server ร่วมกันได้หรือไม่?
สามารถทำได้ แต่ต้องใช้ความระมัดระวัง ให้กำหนด WP_REDIS_PREFIX ที่ไม่ซ้ำกันสำหรับแต่ละเว็บไซต์เพื่อป้องกันชื่อคีย์ซ้ำกัน และใช้ดัชนี WP_REDIS_DATABASE แยกกันเพื่อไม่ให้การล้างข้อมูลของเว็บไซต์หนึ่งส่งผลกระทบต่ออีกเว็บไซต์หนึ่ง สิ่งที่ยังคงใช้ร่วมกันคือหน่วยความจำ โดย maxmemory และการลบข้อมูล (eviction) จะมีผลกับทั้ง instance ดังนั้นเว็บไซต์ที่มีทราฟฟิกสูงอาจทำให้คีย์ของเว็บไซต์ที่มีทราฟฟิกน้อยถูกลบออกไปได้ เว็บไซต์ที่ไม่ต้องการให้ส่งผลกระทบต่อกันจำเป็นต้องใช้ Redis instance แยกกันพร้อมกำหนดขีดจำกัดของตัวเอง
การลบไฟล์ wp-content/object-cache.php ปลอดภัยหรือไม่?
ปลอดภัย ไฟล์นี้เป็น drop-in ไม่ใช่ส่วนหนึ่งของ WordPress core การลบไฟล์นี้จะทำให้ WordPress กลับไปใช้ระบบแคชต่อคำขอ (per-request cache) ตามปกติ เว็บไซต์จะยังคงทำงานได้ตามปกติเพียงแต่จะมีการสอบถามฐานข้อมูลมากขึ้น แนะนำให้ใช้ wp redis disable ซึ่งจะลบไฟล์อย่างสะอาดและรายงาน Object cache disabled. การลบด้วยตนเองเป็นวิธีแก้ไขฉุกเฉินที่ถูกต้องหาก Redis ล่มหรือทำงานผิดปกติจนคุณไม่สามารถเข้าถึงหน้าผู้ดูแลระบบได้