WordPress Object Caching with Redis

John
20 min read

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:

  1. Go to Settings → Redis
  2. Click "Enable Object Cache"
  3. 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:

PolicyBehaviorBest For
noevictionReturn errors, don't evictNot recommended for WordPress
allkeys-lruRemove least recently usedRecommended for WordPress
volatile-lruRemove least recently used (with expire)Mixed use cases
allkeys-lfuRemove least frequently usedHeavy read sites
allkeys-randomRemove random keysNot recommended
volatile-ttlRemove keys expiring soonestNot 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:

  1. Go to Settings → Redis
  2. View "Diagnostics" tab
  3. 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:

  1. KEYS command: Never use KEYS * in production
  2. Large values: Breaking data into smaller chunks
  3. 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

  1. Redis is Essential: For any WordPress site with moderate traffic, Redis provides massive performance gains
  2. Configuration Matters: Proper maxmemory and eviction policy are critical
  3. Monitor Hit Ratio: Aim for 90%+ cache hit rate
  4. Security First: Always use password, bind to localhost, rename dangerous commands
  5. Smart Invalidation: Clear cache when data changes, but don't over-clear
  6. Use Groups: Organize cache keys by type for easier management
  7. Benchmark: Always measure before/after to verify improvements