WordPress Object Caching with Redis
What is Redis?
Redis (Remote Dictionary Server) is an in-memory data structure store used as a database, cache, and message broker.
Simple Explanation
Think of Redis as an extremely fast notepad in your computer's RAM where you can quickly jot down and retrieve information:
Regular Database (MySQL):
Writing: Like writing in a book on a shelf
(must walk to shelf, find book, open it, write, close it)
Reading: Walk to shelf, find book, open it, read, close it
Speed: Milliseconds to seconds
Redis Cache:
Writing: Like writing on a notepad on your desk
Reading: Just look at the notepad
Speed: Microseconds (1000x faster than disk)
Why Redis for WordPress?
The Problem: Database Queries are Slow
WordPress makes MANY database queries for a single page:
Typical WordPress Homepage (without cache):
═══════════════════════════════════════════════════════════
Query 1: Get site options (meta data) - 15ms
Query 2: Get active plugins list - 8ms
Query 3: Get theme options - 5ms
Query 4: Get latest 10 posts - 45ms
Query 5: Get post metadata for each post - 120ms (10 × 12ms)
Query 6: Get author info for each post - 80ms (10 × 8ms)
Query 7: Get categories for each post - 60ms (10 × 6ms)
Query 8: Get featured images - 50ms (10 × 5ms)
Query 9: Get sidebar widgets - 30ms
Query 10: Get menu items - 25ms
Query 11: Get comments count - 40ms
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total Database Time: 478ms
Additional PHP processing: 150ms
Total page generation time: 628ms
The Solution: Redis Object Cache
With Redis, subsequent requests are dramatically faster:
First Request (cache miss):
Database queries: 478ms
Save to Redis: 5ms
Total: 483ms (slightly slower)
Second Request (cache hit):
Redis lookups: 8ms (60x faster!)
Total: 158ms (75% improvement)
Third+ Requests:
Redis lookups: 8ms
Total: 158ms (consistent performance)
How Redis Works
Data Storage Model
Redis stores data as key-value pairs in memory:
┌─────────────────────────────────────────────────────────┐
│ REDIS SERVER │
│ (In RAM - Fast!) │
├─────────────────────────────────────────────────────────┤
│ Key │ Value │
├──────────────────────────────┼──────────────────────────┤
│ "post_123_meta" │ {title: "...", │
│ │ content: "...", │
│ │ author_id: 5} │
├──────────────────────────────┼──────────────────────────┤
│ "user_5_data" │ {name: "John", │
│ │ email: "...", │
│ │ role: "admin"} │
├──────────────────────────────┼──────────────────────────┤
│ "homepage_posts" │ [123, 124, 125, ...] │
├──────────────────────────────┼──────────────────────────┤
│ "site_options" │ {site_name: "...", │
│ │ timezone: "...", │
│ │ permalink: "..."} │
└──────────────────────────────┴──────────────────────────┘
Request Flow with Redis
WordPress Request
↓
Need post data for ID 123
↓
Check Redis: GET "post_123"
↓
Found? ──YES→ Return data (0.5ms) → Continue
│
NO
↓
Query MySQL Database (15ms)
↓
Save to Redis: SET "post_123" {data} EX 3600
↓
Return data → Continue
Installing Redis
On Ubuntu Server
# Update package list
sudo apt update
# Install Redis server
sudo apt install redis-server -y
# Configure Redis for production
sudo nano /etc/redis/redis.conf
Start and Enable Redis:
# Start Redis
sudo systemctl start redis-server
# Enable on boot
sudo systemctl enable redis-server
# Check status
sudo systemctl status redis-server
# Test connection
redis-cli ping
# Should respond: PONG
Start Redis:
docker-compose up -d redis
# Test connection
docker exec -it wordpress_redis redis-cli
> AUTH your_password_here
> PING
PONG
Connecting WordPress to Redis
Step 1: Install Redis PHP Extension
# Ubuntu/Debian
sudo apt install php-redis
# Or install via PECL
sudo pecl install redis
# Restart PHP-FPM
sudo systemctl restart php8.2-fpm
# Verify installation
php -m | grep redis
# Should show: redis
Step 2: Install WordPress Redis Plugin
Option A: Redis Object Cache (Recommended)
# Via WP-CLI
wp plugin install redis-cache --activate
# Or download manually
cd /var/www/html/wp-content/plugins
git clone https://github.com/rhubarbgroup/redis-cache.git
Option B: W3 Total Cache (includes Redis support)
wp plugin install w3-total-cache --activate
# Configure object cache to use Redis in settings
Step 3: Configure WordPress
Add to wp-config.php (before "That's all, stop editing!"):
/**
* Redis Object Cache Configuration
*/
// Redis host
define('WP_REDIS_HOST', '127.0.0.1');
// Redis port (default: 6379)
define('WP_REDIS_PORT', 6379);
// Redis password (if set)
define('WP_REDIS_PASSWORD', 'your_password_here');
// Redis database number (0-15, use different numbers for multiple sites)
define('WP_REDIS_DATABASE', 0);
// Key prefix (useful for multiple WordPress sites sharing one Redis)
define('WP_REDIS_PREFIX', 'mysite');
// Connection timeout in seconds
define('WP_REDIS_TIMEOUT', 1);
// Read timeout in seconds
define('WP_REDIS_READ_TIMEOUT', 1);
// Maximum time-to-live (TTL) for cached objects
define('WP_REDIS_MAXTTL', 86400); // 24 hours
// Use igbinary serializer (faster, requires php-igbinary)
define('WP_REDIS_IGBINARY', true);
// Enable Redis for object cache
define('WP_CACHE_KEY_SALT', 'mysite_');
Step 4: Enable Redis Object Cache
Via WP-CLI (recommended):
# Enable object cache
wp redis enable
# Check status
wp redis status
# View info
wp redis info
# View metrics
wp redis metrics
Via WordPress Admin:
- Go to Settings → Redis
- Click "Enable Object Cache"
- Verify "Status: Connected"
Verify Installation:
# Check Redis is receiving data
redis-cli
> AUTH your_password_here
> KEYS *
# Should show WordPress cache keys
> DBSIZE
# Should show number of keys stored
> INFO stats
# Shows cache statistics
Redis Configuration for WordPress
Memory Optimization
Calculate Required Memory:
Formula: (Average Objects × Average Object Size) × 1.3
Example:
- 10,000 posts
- Average 5 KB per post with metadata
- Safety buffer: 30%
Calculation: (10,000 × 5 KB) × 1.3 = 65 MB
Recommendation: 128 MB minimum (allows for growth)
Set in redis.conf:
# For small site (< 10k posts)
maxmemory 128mb
# For medium site (10k-100k posts)
maxmemory 512mb
# For large site (100k+ posts)
maxmemory 2gb
# For enterprise site
maxmemory 8gb
Eviction Policies
When Redis reaches maxmemory, it must decide what to remove:
| Policy | Behavior | Best For |
|---|---|---|
| noeviction | Return errors, don't evict | Not recommended for WordPress |
| allkeys-lru | Remove least recently used | Recommended for WordPress |
| volatile-lru | Remove least recently used (with expire) | Mixed use cases |
| allkeys-lfu | Remove least frequently used | Heavy read sites |
| allkeys-random | Remove random keys | Not recommended |
| volatile-ttl | Remove keys expiring soonest | Not ideal for WordPress |
Configuration:
maxmemory-policy allkeys-lru
Persistence Configuration
For Pure Cache (Recommended):
# Disable RDB snapshots (no disk writes)
save ""
# Disable AOF (append-only file)
appendonly no
For Cache + Persistence (slower, safer):
# RDB snapshots
save 900 1 # Save if 1 key changed in 15 min
save 300 10 # Save if 10 keys changed in 5 min
save 60 10000 # Save if 10000 keys changed in 1 min
# AOF persistence
appendonly yes
appendfsync everysec # Write to disk every second
Trade-offs:
Pure Cache (No Persistence):
✓ Fastest performance
✓ Lower disk I/O
✗ Lost cache on restart (regenerates quickly)
With Persistence:
✓ Survives restarts
✓ Disaster recovery
✗ Slower (disk writes)
✗ Higher disk I/O
Advanced Redis Usage
Manual Caching in Code
/**
* Cache expensive database query
*/
function get_popular_posts($limit = 10) {
$cache_key = 'popular_posts_' . $limit;
// Try to get from cache
$posts = wp_cache_get($cache_key, 'posts');
if (false === $posts) {
// Cache miss - query database
global $wpdb;
$posts = $wpdb->get_results($wpdb->prepare("
SELECT p.ID, p.post_title, COUNT(c.comment_ID) as comment_count
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->comments} c ON p.ID = c.comment_post_ID
WHERE p.post_status = 'publish'
AND p.post_type = 'post'
GROUP BY p.ID
ORDER BY comment_count DESC
LIMIT %d
", $limit));
// Store in cache for 1 hour
wp_cache_set($cache_key, $posts, 'posts', HOUR_IN_SECONDS);
}
return $posts;
}
Transients with Redis
WordPress transients automatically use Redis when object cache is enabled:
// Set transient (automatically stored in Redis)
set_transient('api_response', $data, HOUR_IN_SECONDS);
// Get transient (automatically from Redis)
$data = get_transient('api_response');
// Delete transient
delete_transient('api_response');
Fragment Caching
Cache specific parts of your page:
/**
* Cache sidebar widget output
*/
function my_cached_sidebar() {
$cache_key = 'sidebar_output';
// Try to get cached HTML
$output = wp_cache_get($cache_key, 'fragments');
if (false === $output) {
// Start output buffering
ob_start();
// Generate sidebar
dynamic_sidebar('main-sidebar');
// Get buffered output
$output = ob_get_clean();
// Cache for 1 hour
wp_cache_set($cache_key, $output, 'fragments', HOUR_IN_SECONDS);
}
echo $output;
}
Monitoring Redis Performance
Redis CLI Commands
# Connect to Redis
redis-cli -h 127.0.0.1 -p 6379 -a your_password
# Get server information
INFO
# Get memory statistics
INFO memory
# Get key statistics
INFO keyspace
# Get statistics
INFO stats
# Get current number of keys
DBSIZE
# Get sample of keys
KEYS wp_* | head -20
# Monitor commands in real-time
MONITOR
# Get specific key info
TYPE keyname
TTL keyname
GET keyname
# Memory usage of specific key
MEMORY USAGE keyname
Key Metrics to Monitor
# Check hit rate
INFO stats | grep keyspace
# Example output:
# keyspace_hits:523847
# keyspace_misses:12893
# Calculate hit ratio:
# Hit Ratio = hits / (hits + misses)
# 523847 / (523847 + 12893) = 97.6% (excellent!)
Good Targets:
- Hit Ratio: > 90%
- Memory Usage: < 80% of maxmemory
- Evicted Keys: < 1% of total keys
- Connected Clients: < maxclients setting
WordPress Plugin Statistics
Via WP-CLI:
# View Redis metrics
wp redis metrics
# Output shows:
# - Hit ratio
# - Memory usage
# - Number of keys
# - Uptime
Via Admin Dashboard:
- Go to Settings → Redis
- View "Diagnostics" tab
- Check:
- Status: Connected
- Hit Ratio: > 90%
- Memory Usage
- Uptime
Troubleshooting
Issue 1: Connection Failed
Error: Cannot connect to Redis server
Diagnosis:
# Check if Redis is running
sudo systemctl status redis-server
# Test connection manually
redis-cli ping
# Should respond: PONG
# Check Redis is listening
sudo netstat -tulpn | grep 6379
Solutions:
# Start Redis
sudo systemctl start redis-server
# Check configuration
sudo nano /etc/redis/redis.conf
# Ensure: bind 127.0.0.1 ::1
# Check PHP extension
php -m | grep redis
# Install if missing
sudo apt install php-redis
sudo systemctl restart php8.2-fpm
Issue 2: Authentication Failed
Error: NOAUTH Authentication required
Solution in wp-config.php:
// Add password
define('WP_REDIS_PASSWORD', 'your_password_here');
Or remove password from redis.conf:
# Comment out requirepass
# requirepass your_password_here
Restart Redis:
sudo systemctl restart redis-server
Issue 3: Memory Issues
Error: OOM command not allowed when used memory > 'maxmemory'
Diagnosis:
redis-cli
> INFO memory
# Check:
# used_memory_human
# maxmemory_human
Solutions:
Option 1: Increase memory:
# In redis.conf
maxmemory 512mb # Increase from 256mb
Option 2: Enable eviction:
maxmemory-policy allkeys-lru
Option 3: Flush cache:
# Via WP-CLI
wp cache flush
# Or via Redis
redis-cli FLUSHDB
Issue 4: Stale Data
Problem: Cached data not updating after changes
Solution 1: Clear specific cache:
// In your save_post hook
add_action('save_post', 'clear_post_cache');
function clear_post_cache($post_id) {
wp_cache_delete($post_id, 'posts');
wp_cache_delete('homepage_posts', 'queries');
}
Solution 2: Flush entire cache:
wp cache flush
Solution 3: Set shorter TTL:
// Instead of:
wp_cache_set('key', $data, 'group', DAY_IN_SECONDS);
// Use:
wp_cache_set('key', $data, 'group', HOUR_IN_SECONDS);
Issue 5: High CPU Usage
Diagnosis:
# Check Redis CPU
top -p $(pgrep redis-server)
# Check slow log
redis-cli SLOWLOG GET 10
Common Causes:
- KEYS command: Never use
KEYS *in production - Large values: Breaking data into smaller chunks
- No eviction policy: Set maxmemory-policy
Solutions:
# In redis.conf
slowlog-log-slower-than 10000 # Log queries > 10ms
slowlog-max-len 128 # Keep last 128 slow queries
# Enable lazy freeing
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
Key Takeaways
- Redis is Essential: For any WordPress site with moderate traffic, Redis provides massive performance gains
- Configuration Matters: Proper maxmemory and eviction policy are critical
- Monitor Hit Ratio: Aim for 90%+ cache hit rate
- Security First: Always use password, bind to localhost, rename dangerous commands
- Smart Invalidation: Clear cache when data changes, but don't over-clear
- Use Groups: Organize cache keys by type for easier management
- Benchmark: Always measure before/after to verify improvements