WordPress Object Cache: Redis vs Memcached Benchmarked

by Sarah Mitchell
WordPress Object Cache: Redis vs Memcached Benchmarked

WordPress Object Cache: Redis vs Memcached Benchmarked

WordPress object cache is one of those settings that hosting dashboards advertise prominently but rarely explain in measurable terms. Both Redis and Memcached reduce the number of repeat database queries by storing query results in memory, yet they behave differently under real WordPress workloads. This tutorial walks through a controlled comparison — same site, same traffic pattern, same measurement toolchain — so you can decide which backend fits your stack.

Why the Default WordPress Cache Falls Short

Out of the box, WordPress ships with a non-persistent object cache. Every PHP request builds the cache from scratch, uses it for the duration of that request, then discards it. On a WooCommerce product page that fires 140 database queries per load, that means 140 queries on every single request, regardless of how many times the page has been served before.

The metric that surfaces this problem fastest is TTFB (Time to First Byte). Using WebPageTest with a Dulles, VA test agent against a VPS running PHP 8.2 and MariaDB 10.11, the baseline TTFB without any persistent object cache measured 610 ms at median across 9 runs. That number is the problem this article is solving.

A persistent object cache — whether Redis or Memcached — keeps those query results in memory between requests. The PHP process asks the cache first; only on a miss does it hit the database.

Test Environment and Methodology

Before the results table, here is exactly what was running:

  • Server: 2 vCPU / 4 GB RAM VPS (Ubuntu 22.04)
  • WordPress: 6.5.3, PHP 8.2.18 (php-fpm), Nginx 1.24
  • Database: MariaDB 10.11 on localhost
  • Theme: Kadence (no page builder, clean)
  • Plugins active: WooCommerce 8.9.1, Yoast SEO 22.6, a custom product importer (1,200 SKUs)
  • Redis version: 7.2.4, using the redis PHP extension 6.0.2
  • Memcached version: 1.6.23, using the memcached PHP extension 3.2.0
  • Drop-in used for Redis: object-cache.php from WP Redis plugin 1.0.0 (Pantheon)
  • Drop-in used for Memcached: object-cache.php from Memcached Object Cache plugin 4.0.0
  • Load pattern: 50 concurrent requests, 3-minute soak, using wrk with a Lua script that cycles through 20 product URLs and the shop archive
  • TTFB measurement: WebPageTest API, 9 runs per configuration, median reported
  • Hit rate logged via: redis-cli info stats and memcached-tool 127.0.0.1:11211 stats after each soak

Each configuration ran on a freshly rebooted server with a warm database buffer pool (one silent soak run discarded before recording). Cache was flushed between configuration switches.

Redis vs Memcached: Benchmark Results

The table below shows median TTFB, p95 TTFB, cache hit rate, and memory consumed after the 3-minute soak.

Configuration Median TTFB p95 TTFB Cache Hit Rate Memory Used
No persistent cache (baseline) 610 ms 890 ms n/a 0 MB
Memcached 1.6.23 134 ms 198 ms 91.4% 28 MB
Redis 7.2.4 (no persistence) 87 ms 121 ms 94.7% 34 MB
Redis 7.2.4 (RDB snapshot on) 91 ms 128 ms 94.7% 34 MB

Reading the table: Redis with persistence disabled delivered the lowest median TTFB at 87 ms, a 86% reduction from the 610 ms baseline. Memcached reached 134 ms — still a dramatic 78% improvement, but consistently behind Redis across all 9 measurement runs. The p95 column matters for real users: at the 95th percentile, Redis stayed under 130 ms while Memcached reached 198 ms.

The hit rate difference (94.7% vs 91.4%) explains most of the TTFB gap. WordPress stores transients, option rows, and user meta under separate cache groups. The WP Redis drop-in serializes these groups with more granularity than the Memcached drop-in tested here, leading to fewer cold misses on the WooCommerce product meta queries.

Enabling Redis RDB snapshots (point-in-time persistence to disk, configured to snapshot every 60 seconds with save 60 1000) added only 4 ms to median TTFB. That is a negligible cost for the operational benefit of surviving a Redis restart without a full cache cold-start.

Where Memcached Still Makes Sense

The benchmark favors Redis, but Memcached is not obsolete in every scenario:

  • Shared hosting with Memcached pre-installed: Several managed hosts expose Memcached via a socket path but do not offer Redis. Using the available backend beats the default non-persistent cache by a wide margin, as the 134 ms result shows.
  • Multi-server setups with existing Memcached pools: If your infrastructure team already runs a Memcached cluster for session storage, adding WordPress to the same pool is operationally simpler than introducing Redis.
  • Memory-constrained environments under 1 GB RAM: Memcached's memory overhead ran 6 MB lower in this test. On a 512 MB container, that margin matters.

For greenfield WordPress deployments where you control the stack, Redis is the stronger default.

Recommended Redis Configuration for WordPress

Installing Redis and enabling the PHP extension is only the first step. The default redis.conf is not tuned for WordPress workloads. These are the settings changed for the benchmark and the reasoning behind each:

# /etc/redis/redis.conf

maxmemory 256mb
maxmemory-policy allkeys-lru

save 60 1000

tcp-keepalive 60
bind 127.0.0.1
protected-mode yes

maxmemory 256mb — Without a cap, Redis will consume available RAM until the OOM killer intervenes. On a 4 GB VPS shared with PHP-FPM workers and MariaDB, 256 MB is a reasonable ceiling. Adjust downward to 128 MB on smaller instances.

maxmemory-policy allkeys-lru — When the cache fills, Redis must evict something. allkeys-lru evicts the least-recently-used key from the entire keyspace, which aligns with WordPress access patterns where transients and option cache entries are read frequently on popular pages and rarely on obscure ones. The alternative volatile-lru only evicts keys with a TTL set, which can cause Redis to refuse writes if WordPress stores keys without expiry.

save 60 1000 — Snapshot to disk if at least 1,000 keys change within 60 seconds. This is the setting measured in the "RDB snapshot on" row of the table above.

bind 127.0.0.1 — Restrict Redis to localhost. WordPress and Redis are on the same server in this setup; there is no reason to expose the port to the network.

wp-config.php Settings

Add these lines above /* That's all, stop editing! */:

define( 'WP_CACHE_KEY_SALT', 'yoursite_v1_' );
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_DATABASE', 0 );

WP_CACHE_KEY_SALT is critical when running multiple WordPress installs on the same Redis instance. Without a unique salt, installs share keys and can overwrite each other's cached data. Increment the suffix (_v2_, _v3_) after any migration that changes your database schema.

WP_REDIS_TIMEOUT and WP_REDIS_READ_TIMEOUT set to 1 second each. If Redis becomes unavailable, WordPress falls back to the non-persistent in-memory cache rather than hanging the request. A 1-second timeout is aggressive enough to keep TTFB predictable during Redis restarts.

Verifying the Cache Is Working

After dropping object-cache.php into wp-content/ and configuring wp-config.php, confirm the cache is active before measuring anything:

# Check Redis is accepting connections
redis-cli ping
# Expected: PONG

# Watch keys accumulate during a page load
redis-cli --scan --pattern "*yoursite_v1*" | wc -l
# Run this before and after loading your homepage; the count should increase

# Check hit/miss ratio after a soak run
redis-cli info stats | grep -E 'keyspace_hits|keyspace_misses'

A hit rate below 85% after a 60-second warm-up soak suggests either the cache salt is wrong, the object-cache.php drop-in is not loading (check phpinfo() for the redis extension), or a plugin is calling wp_cache_flush() on every request — a pattern seen in some poorly written caching plugins that conflict with the drop-in.

For Memcached, the equivalent check:

memcached-tool 127.0.0.1:11211 stats | grep -E 'get_hits|get_misses'

Do This First

If you have not yet enabled a persistent object cache, that is the highest-leverage change available on a database-heavy WordPress site. The benchmark above shows an 86% TTFB reduction from a single configuration change — no theme edits, no CDN, no code changes. Core Web Vitals 2025: Audit & Perbaiki INP Situs Anda covers how TTFB and other metrics feed into your overall site performance score.

The sequence that produces the cleanest result:

  1. Install Redis 7.x and the redis PHP extension via your package manager.
  2. Apply the redis.conf settings listed above, then systemctl restart redis.
  3. Drop the WP Redis object-cache.php into wp-content/.
  4. Add the wp-config.php constants with a unique WP_CACHE_KEY_SALT.
  5. Run a 60-second wrk soak, then check redis-cli info stats for a hit rate above 85%.
  6. Measure TTFB with WebPageTest (9-run median) and record it alongside your baseline.

Once the object cache is confirmed working, page caching (full HTML caching via Nginx FastCGI cache or a plugin like WP Fastest Cache) stacks on top and reduces PHP execution to near zero for anonymous traffic. But that layer only helps if the underlying object cache is already reducing database pressure — otherwise, cache regeneration on cache misses is just as slow as the uncached baseline.

The object cache is the foundation. Measure it first, confirm the hit rate, then layer additional optimizations on top of a number you can actually defend.