SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor

วิธีติดตั้ง Redis Object Cache บน WordPress VPS

เรียนรู้วิธีตั้งค่า Redis บน VPS เพื่อเพิ่มความเร็วให้ WordPress โดยการปรับแต่ง bind localhost, กำหนด maxmemory และเลือก eviction policy ที่เหมาะสม พร้อมวิธีตรวจสอบสถานะการทำงานจริง

สิ่งที่ Redis object cache ทำให้กับ WordPress

Redis object cache สำหรับ WordPress จะจัดเก็บผลลัพธ์ของการสืบค้นฐานข้อมูลไว้ในหน่วยความจำ เพื่อให้การร้องขอครั้งถัดไปอ่านข้อมูลจาก Redis แทนที่จะต้องสอบถาม MySQL อีกครั้ง WordPress มีระบบ object cache อยู่ใน core แล้วคือ WP_Object_Cache แต่ระบบดังกล่าวทำงานอยู่ในหน่วยความจำของ PHP และจะถูกล้างทิ้งเมื่อการร้องขอนั้นสิ้นสุดลง การใช้ไฟล์ drop-in จะเข้ามาแทนที่ระบบเดิมด้วยระบบที่สื่อสารกับ Redis ทำให้แคชยังคงอยู่ต่อเนื่องจากการร้องขอหนึ่งไปยังอีกการร้องขอหนึ่ง

Object caching ไม่ใช่ page caching และความแตกต่างนี้เป็นตัวตัดสินว่าคู่มือนี้มีประโยชน์ต่อคุณหรือไม่ Page cache จะจัดเก็บ HTML ที่ประมวลผลเสร็จสิ้นแล้วของ URL นั้นๆ และส่งคืนข้อมูลโดยไม่ต้องรัน PHP เลย ซึ่งเร็วกว่าสิ่งที่ Redis ทำได้ และใช้ได้ผลดีกับผู้เยี่ยมชมที่ไม่ได้ล็อกอิน ทันทีที่มีคนล็อกอิน เพิ่มสินค้าลงในตะกร้า หรือเปิดหน้า admin ระบบ page cache จะหยุดทำงานและ WordPress จะประมวลผลการร้องขอทั้งหมดใหม่ตั้งแต่ต้น ทั้งการ bootstrap, plugins และ queries ส่วน object cache จะช่วยให้การร้องขอดังกล่าวมีต้นทุนการประมวลผลที่ต่ำลง มันจึงเป็นเครื่องมือสำหรับ traffic ที่ page cache เข้าไม่ถึง เช่น เซสชันของผู้ที่ล็อกอิน, ตะกร้าสินค้า, การชำระเงิน และ wp-admin ซึ่งสำหรับร้านค้า WooCommerce นี่คือส่วนใหญ่ของ traffic ที่ใช้ทรัพยากรสูง

ทั้งสองระบบสามารถทำงานร่วมกันได้ และบนเว็บไซต์ที่มีการใช้งานสูงควรมีทั้งคู่ คุณต้องชัดเจนว่ากำลังแก้ไขปัญหาใดอยู่ เว็บไซต์ประชาสัมพันธ์ที่มีผู้อ่านทั่วไปจะได้รับความเร็วเกือบทั้งหมดจาก page cache และการเพิ่ม Redis เข้าไปแทบจะไม่ช่วยให้เห็นความแตกต่าง

ข้อจำกัดที่ต้องแจ้งให้ทราบก่อนเริ่มคือ Object cache ไม่ได้ทำให้ query ที่ช้ากลายเป็น query ที่เร็ว แต่มันช่วยลดการรัน query ซ้ำที่เคยเกิดขึ้นไปแล้ว การร้องขอครั้งแรกหลังจากที่ข้อมูลไม่อยู่ในแคช (miss) จะต้องเสียเวลาประมวลผลเต็มจำนวน ดังนั้นหากมีปลั๊กอินที่รัน query โดยไม่มีการทำ index มันก็จะยังคงรัน query นั้นหนึ่งครั้งต่ออายุการใช้งานของแคชอยู่ดี

สิ่งที่ต้องเตรียมก่อนเริ่มต้น

  • VPS ที่รัน Linux พร้อมสิทธิ์ shell และ sudo ไม่จำเป็นต้องใช้ control panel
  • WordPress ที่ทำงานผ่าน PHP-FPM เช่น บน LAMP stack บน Ubuntu 24.04
  • ติดตั้ง WP-CLI ไว้บนเครื่อง ทุกขั้นตอนในที่นี้สามารถทำผ่านหน้าจอผู้ดูแลระบบได้ แต่การใช้ shell จะรวดเร็วกว่า
  • ติดตั้ง Redis ไว้บนเครื่องเดียวกับ PHP เนื่องจากหัวใจสำคัญคือการลด latency ซึ่งการส่งข้อมูลผ่านเครือข่ายจะทำให้ประสิทธิภาพลดลง

คำสั่งด้านล่างนี้เขียนขึ้นสำหรับ Ubuntu 24.04 ที่ใช้ PHP 8.3 และผู้ใช้งานเว็บคือ www-data โปรดปรับเปลี่ยนเวอร์ชัน PHP และชื่อผู้ใช้งานให้ตรงกับระบบของคุณ ให้รันคำสั่ง wp จากไดเรกทอรีของ WordPress ซึ่งเป็นตำแหน่งที่เก็บไฟล์ wp-config.php ไว้

การติดตั้ง Redis และ PHP extension

sudo apt update
sudo apt install -y redis-server php-redis
sudo systemctl enable --now redis-server
redis-cli ping

redis-cli ping ควรตอบกลับด้วย PONG หากแสดงผลเป็น Could not connect to Redis at 127.0.0.1:6379: Connection refused แสดงว่าเซิร์ฟเวอร์ไม่ได้ทำงานอยู่ ดังนั้นให้อ่าน systemctl status redis-server ก่อนดำเนินการต่อ

php-redis คือ PhpRedis ซึ่งเป็น C extension จาก PECL โดยมีความเร็วสูงกว่า Predis ที่เขียนด้วย PHP ล้วน และปลั๊กอินจะเรียกใช้งานโดยอัตโนมัติเมื่อตรวจพบ PHP-FPM จะโหลด extension ในช่วงเริ่มต้นทำงาน ดังนั้น extension ใหม่จะไม่ถูกตรวจพบจนกว่าคุณจะรีสตาร์ท 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

หาก 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 และการเข้าถึงจะถูกควบคุมด้วยสิทธิ์ของไฟล์แทนที่จะเป็นกฎของไฟร์วอลล์ที่คุณอาจเผลอไปแก้ไขในภายหลัง

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 จะเข้ามาหยุดการทำงานของ process ซึ่งมักจะเป็น process ที่ใช้หน่วยความจำมากที่สุด และบนเซิร์ฟเวอร์ WordPress มักจะเป็น MySQL journalctl -k | grep -i "out of memory" จะแสดงการหยุดทำงานดังกล่าวหลังจากที่เกิดขึ้นแล้ว ซึ่งในตอนนั้นเว็บไซต์จะล่มไปเรียบร้อยแล้ว

ให้เริ่มจาก RAM ทั้งหมดแล้วหักลบส่วนที่จำเป็นออก MySQL หรือ MariaDB จะจองหน่วยความจำ innodb_buffer_pool_size บวกกับ buffer สำหรับแต่ละการเชื่อมต่อ PHP-FPM จะมีค่าใช้จ่ายเท่ากับ pm.max_children คูณด้วยขนาด resident size จริงของ worker หนึ่งตัว ซึ่งโดยทั่วไปจะอยู่ที่ 64 MB ถึง 128 MB สำหรับเว็บไซต์ที่ติดตั้งปลั๊กอินจำนวนมาก ส่วน kernel และ web server ต้องการหน่วยความจำอีกสองสามร้อยเมกะไบต์ ส่วนที่เหลือคือเพดานหน่วยความจำของคุณ และ Redis จะได้รับส่วนแบ่งจากตรงนั้น

ตัวอย่างการจัดสรรงบประมาณสำหรับ VPS ขนาด 4 GB ที่รันร้านค้าออนไลน์หนึ่งแห่ง

ตัวเลขเหล่านี้เป็นเพียงตัวอย่าง ไม่ใช่ค่าที่วัดได้จากเซิร์ฟเวอร์ของคุณ ให้แทนที่แต่ละค่าด้วยตัวเลขที่เซิร์ฟเวอร์ของคุณรายงานจริง

  • MariaDB พร้อม buffer pool ขนาด 1 GB: 1024 MB
  • PHP-FPM, worker 10 ตัว ตัวละ 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-lru

redis-cli config set maxmemory 256mb จะมีผลทันทีและจะถูกลืมเมื่อรีสตาร์ทเครื่อง ซึ่งเป็นกับดักเดียวกับการแก้ไข sysctl -w โดยตรง ให้แก้ไขไฟล์ จากนั้น sudo systemctl restart redis-server แล้วตรวจสอบค่าที่ตั้งไว้ การตั้งค่ากำแพงชั้นที่สองเป็นสิ่งที่ควรทำ: การจำกัด MemoryMax บน systemd unit จะช่วยป้องกันไม่ให้ Redis ที่ตั้งค่าผิดพลาดทำให้ทั้งเซิร์ฟเวอร์ล่ม ให้ตั้งค่าไว้สูงกว่า maxmemory แต่อย่าตั้งให้เท่ากัน เพราะขีดจำกัดของ cgroup จะทำให้ process ถูกสั่งหยุดทำงานแทนที่จะเป็นการลบ key ออก หาก Redis รันอยู่ใน container ข้างๆ WordPress ให้ใส่ตัวเลขเดียวกันนี้ใน การจำกัดหน่วยความจำในไฟล์ Compose และเหตุผลเดียวกันนี้เป็นตัวกำหนดการตัดสินใจ การรันฐานข้อมูลใน Docker หรือบน host

เลือกนโยบายการไล่ข้อมูล (eviction policy) อย่างตั้งใจ

Redis ที่ติดตั้งใหม่จะใช้ค่าเริ่มต้นเป็น noeviction ให้ตรวจสอบค่าของคุณด้วยคำสั่งนี้:

redis-cli config get maxmemory-policy

ภายใต้ค่า noeviction อินสแตนซ์ที่เต็มจะหยุดรับการเขียนข้อมูลและตอบกลับด้วยข้อความนี้:

(error) OOM command not allowed when used memory > 'maxmemory'.

บรรทัดเดียวนี้คือรูปแบบความล้มเหลวที่แย่ที่สุดในคู่มือนี้ เพราะเว็บไซต์ไม่ได้ล่ม แต่จะทำงานช้าลง การเขียนข้อมูลลงแคชทุกครั้งจะล้มเหลว ทำให้ WordPress ต้องกลับไปดึงข้อมูลจากฐานข้อมูลแทน จากนั้นจึงพยายามจัดเก็บข้อมูลลงแคชอีกครั้งในคำขอถัดไปและล้มเหลวซ้ำเดิม เว็บไซต์จะต้องแบกรับภาระงานฐานข้อมูลทั้งหมดเหมือนเดิม บวกกับ การรับส่งข้อมูลไปยัง Redis สำหรับทุกคีย์ที่เรียกใช้ ไม่มีส่วนใดในหน้าผู้ดูแลระบบของ WordPress ที่จะแจ้งให้คุณทราบว่าเหตุการณ์นี้กำลังเกิดขึ้น ข้อความนี้จะปรากฏใน PHP error log ดังนั้นให้ใช้ grep ค้นหา OOM command not allowed เมื่อเว็บไซต์ทำงานช้าลงหลังจากที่คุณเพิ่มระบบแคชเข้าไป

allkeys-lru คือค่าเริ่มต้นที่เหมาะสมสำหรับกรณีนี้ Redis จะลบเฉพาะคีย์ที่ไม่ได้ถูกใช้งานนานที่สุด (Least Recently Used) ออกเมื่อหน่วยความจำเต็ม ซึ่งเป็นสิ่งที่ object cache ต้องการพอดี เพราะทุกค่าในแคชคือสำเนาของข้อมูลที่มีอยู่ใน MySQL อยู่แล้ว การสูญเสียคีย์หนึ่งรายการมีต้นทุนเพียงแค่การคิวรีฐานข้อมูลหนึ่งครั้ง แต่การปฏิเสธการเขียนข้อมูลจะมีต้นทุนเท่ากับการคิวรีทุกครั้งในทุกคำขอ จนกว่าจะมีคนสังเกตเห็นปัญหา

หลีกเลี่ยงนโยบายกลุ่ม volatile-* สำหรับงานนี้ เพราะนโยบายเหล่านี้จะพิจารณาเฉพาะคีย์ที่มีการกำหนดวันหมดอายุ (expiry) เท่านั้น และ 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 จากไฟล์ที่มีอายุ 20 นาทีจะได้ค่าที่เก่าเกินไปซึ่ง WordPress จะยังคงเชื่อถือข้อมูลนั้น

Snapshots ยังมีต้นทุนในการทำงาน BGSAVE จะทำการ fork กระบวนการทำงาน และกลไก copy-on-write อาจทำให้การใช้หน่วยความจำพุ่งสูงขึ้นอย่างรวดเร็วในขณะที่ child process กำลังเขียนข้อมูล บน 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.

หากต้องการปิด 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 รวมถึงนโยบายการลบข้อมูลจะถูกนำไปใช้กับทั้ง instance ไม่ใช่แค่ database index ใด index หนึ่ง การรันสอง instance บนสอง socket จึงเป็นวิธีแก้ปัญหาที่สะอาดที่สุด

ติดตั้งปลั๊กอินและทำความเข้าใจเกี่ยวกับ drop-in

wp plugin install redis-cache --activate
wp redis enable
wp redis status

wp 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 จะบังคับให้ key ทุกตัวหมดอายุตามจำนวนวินาทีที่กำหนด คุณไม่จำเป็นต้องใช้ค่านี้หากใช้ allkeys-lru และจะมีประโยชน์ในกรณีที่คุณต้องการกำหนดขีดจำกัดสูงสุดที่แน่นอนว่าค่าที่แคชไว้นั้นจะเก่าได้มากที่สุดเท่าใด

การใช้ Redis หนึ่งอินสแตนซ์กับหลายเว็บไซต์: การใช้ prefix และฐานข้อมูล

โดยค่าเริ่มต้น Redis จะมีฐานข้อมูลแบบระบุหมายเลขมาให้ 16 ฐานข้อมูล และมี keyspace แบบแบนภายในแต่ละฐานข้อมูล หากติดตั้ง WordPress สองชุดโดยชี้ไปที่ฐานข้อมูลหมายเลข 0 โดยไม่มีการกำหนด prefix ทั้งสองจะเขียนข้อมูลลงในชื่อคีย์เดียวกันในพื้นที่เดียวกัน ทำให้เว็บไซต์หนึ่งสามารถอ่านค่า options ของอีกเว็บไซต์หนึ่งและนำไปแสดงผลได้ ดังนั้นควรตั้งค่า prefix แยกกันสำหรับแต่ละเว็บไซต์

define( 'WP_REDIS_PREFIX', 'shopA_prod:' );
define( 'WP_REDIS_DATABASE', 1 );

Prefix จะทำหน้าที่แยกชื่อคีย์ออกจากกัน ส่วนดัชนีฐานข้อมูลจะทำหน้าที่แยก keyspace ซึ่งมีความสำคัญเมื่อต้องการล้างข้อมูล (flush): การล้างข้อมูลในดัชนีหนึ่งจะไม่ส่งผลกระทบต่อดัชนีอื่น ปลั๊กอินยังได้ระบุถึง WP_REDIS_SELECTIVE_FLUSH ซึ่งจะลบเฉพาะคีย์ที่ตรงกับ prefix ของคุณแทนการลบข้อมูลทั้งฐานข้อมูล แต่จะมีข้อเสียคือต้องใช้การสแกนเพื่อค้นหาคีย์เหล่านั้น

สิ่งที่ prefix และดัชนีไม่สามารถแยกออกจากกันได้คือหน่วยความจำ การตั้งค่า maxmemory และนโยบายการไล่ข้อมูล (eviction policy) จะมีผลกับทั้งอินสแตนซ์ ดังนั้นเว็บไซต์ที่มีการใช้งานสูงอาจทำให้คีย์ของเว็บไซต์ที่มีการใช้งานน้อยถูกลบออกไปโดยที่ไม่มีเว็บไซต์ใดแจ้งเตือน หากเว็บไซต์เหล่านั้นไม่ต้องการให้ส่งผลกระทบต่อกัน จำเป็นต้องแยกอินสแตนซ์ของ Redis โดยแต่ละอินสแตนซ์ต้องมี socket และขีดจำกัดหน่วยความจำของตนเอง

แยกแคชของ staging ออกจาก production

โดยปกติแล้ว staging site คือสำเนาของไฟล์และฐานข้อมูลจาก production ซึ่งหมายความว่ามันคือสำเนาของ wp-config.php ที่ใช้ prefix และ index ของฐานข้อมูลชุดเดียวกัน หากชี้ staging ไปที่ Redis ตัวเดียวกับ production มันจะเขียนทับ 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 ที่เพิ่มขึ้นในขณะที่คุณคลิกใช้งานเว็บไซต์คือหลักฐานยืนยัน หากมี drop-in ที่ถูกต้องแต่จำนวน key เป็นศูนย์ แสดงว่าการเชื่อมต่อกำลังล้มเหลวแบบเงียบๆ หรือ 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 ทำงานช้าลง

การใช้งาน Redis ทั้ง instance ด้วยนโยบาย (policy) ที่ไม่เหมาะสมเป็นสาเหตุหลัก ซึ่งได้กล่าวถึงไปแล้วข้างต้น โดยจะพบ OOM command not allowed when used memory > 'maxmemory'. ใน log และเว็บไซต์ต้องเสียทรัพยากรให้กับทั้งฐานข้อมูลและแคชพร้อมกัน

การวาง Redis ไว้บนโฮสต์อื่นเป็นสาเหตุที่สอง WordPress มีการเรียกใช้งาน object cache หลายร้อยครั้งในหนึ่ง request หากหนึ่ง request มีการเรียก 500 ครั้ง และแต่ละครั้งใช้เวลาเดินทางไป-กลับ 1 ms จะเท่ากับต้องรอถึงครึ่งวินาที ซึ่งจะไม่เกิดขึ้นหากใช้ local socket ควรเก็บ Redis ไว้บนเครื่องเดียวกัน หรือบนเครือข่ายส่วนตัวที่มีค่า latency ต่ำกว่าระดับมิลลิวินาที

ตาราง options ที่มีข้อมูล autoload ขนาดใหญ่เกินไปเป็นสาเหตุที่สาม ซึ่งพบได้บ่อยในเว็บไซต์เก่า WordPress จะแคชตัวเลือกที่ตั้งค่าเป็น autoload ทั้งหมดไว้ใน key เดียว ดังนั้นข้อมูลขนาด 1 MB จะถูกส่งผ่านการเชื่อมต่อในทุกๆ 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' จะแสดงผลน้อยกว่าความเป็นจริงในการติดตั้งเวอร์ชันใหม่ หากข้อมูลเกิน 1 MB ถือเป็นปัญหาที่ต้องแก้ไขที่ตาราง options ไม่ใช่ที่ Redis

การ restart จะทำให้ข้อมูลทั้งหมดหายไป ดังนั้นช่วงเวลาไม่กี่นาทีหลังจาก systemctl restart redis-server จะเกิด cache miss ทั้งหมดและเป็นการทำงานของฐานข้อมูลล้วนๆ ควรทำการ restart ในช่วงที่มีผู้ใช้งานน้อย และ object cache ไม่สามารถหยุดการทำงานของ wp-cron.php ที่เกิดขึ้นเมื่อผู้เข้าชมโหลดหน้าเว็บ ซึ่งเป็นสาเหตุหนึ่งที่ทำให้ request ช้าลง ควร ย้าย WP-Cron ไปใช้ระบบ cron job จริง ในขณะที่คุณกำลังจัดการส่วนนี้อยู่

การดูแลรักษา

ให้ล้างแคชหลังจาก deploy ที่มีการเปลี่ยนแปลงตัวเลือกหรือโค้ดธีมด้วย wp cache flush หาก drop-in ไม่ได้อัปเดตตัวเอง ให้รัน wp redis update-dropin หลังจากการอัปเดตปลั๊กอิน เนื่องจาก drop-in จากปลั๊กอินเวอร์ชันเก่าที่ทำงานร่วมกับปลั๊กอินเวอร์ชันใหม่เป็นสาเหตุหลักของพฤติกรรมที่ผิดปกติ ให้เฝ้าดูเซิร์ฟเวอร์แบบเรียลไทม์ด้วย redis-cli --stat ซึ่งจะแสดงผลหนึ่งบรรทัดต่อวินาที ส่วน redis-cli monitor จะแสดงทุกคำสั่งและใช้ CPU จริงบน instance ที่มีภาระงานสูง ดังนั้นให้ใช้เพียงไม่กี่วินาทีในขณะที่คุณกำลังจำลองปัญหา แล้วจึงหยุดการทำงาน

ตัวเลขสุดท้ายที่ควรทราบคือ 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 ที่โหลดอัตโนมัติ (autoloaded) ขนาดหลายเมกะไบต์ซึ่งต้องรับส่งผ่านการเชื่อมต่อในทุกคำขอ

เว็บไซต์ WordPress หลายไซต์สามารถใช้ Redis server ร่วมกันได้หรือไม่?

สามารถทำได้หากดำเนินการด้วยความระมัดระวัง โดยให้กำหนด WP_REDIS_PREFIX ที่ไม่ซ้ำกันสำหรับแต่ละไซต์เพื่อป้องกันชื่อคีย์ซ้ำกัน และใช้ดัชนี WP_REDIS_DATABASE แยกกันเพื่อไม่ให้การล้างข้อมูล (flush) ของไซต์หนึ่งส่งผลกระทบต่ออีกไซต์หนึ่ง สิ่งที่ยังคงใช้ร่วมกันคือหน่วยความจำ โดย maxmemory และนโยบายการลบข้อมูล (eviction) จะมีผลกับทั้ง instance ดังนั้นไซต์ที่มีทราฟฟิกสูงอาจทำให้คีย์ของไซต์ที่มีทราฟฟิกน้อยถูกลบออกได้ ไซต์ที่ไม่ต้องการให้ส่งผลกระทบต่อกันจำเป็นต้องใช้ Redis instance แยกกันพร้อมกำหนดขีดจำกัดของตนเอง

การลบไฟล์ wp-content/object-cache.php ปลอดภัยหรือไม่?

ปลอดภัย ไฟล์นี้เป็นเพียง drop-in ไม่ใช่ส่วนหนึ่งของ WordPress core การลบไฟล์นี้จะทำให้ WordPress กลับไปใช้ระบบแคชแบบต่อคำขอ (per-request cache) ตามปกติ เว็บไซต์จะยังคงทำงานได้ตามปกติเพียงแต่จะมีการสอบถามฐานข้อมูล (database queries) เพิ่มขึ้น แนะนำให้ใช้ wp redis disable ซึ่งจะลบไฟล์อย่างถูกต้องและรายงานสถานะ Object cache disabled. การลบด้วยตนเองเป็นวิธีแก้ไขปัญหาฉุกเฉินที่ถูกต้องหาก Redis ล่มหรือทำงานผิดปกติจนคุณไม่สามารถเข้าถึงหน้าผู้ดูแลระบบได้