WordPress Object Cache: Redis vs Memcached Benchmarked
On an unoptimized WordPress site running WooCommerce with 4,000 products, the database handles roughly 80–120 queries per page load. Every one of those queries is a round-trip: PHP asks MySQL a question, MySQL reads from disk or its own buffer, and PHP waits. Object caching short-circuits that cycle by storing query results in memory so subsequent requests skip the database entirely.
The two tools most commonly dropped into managed WordPress stacks are Redis and Memcached. Hosts mention both in their feature lists, but few publish side-by-side numbers under the same workload. This article documents a controlled test on identical server configurations so you can make an informed choice rather than defaulting to whichever one your host enables by default.
What Object Caching Actually Does in WordPress
WordPress ships with a built-in object cache API (WP_Object_Cache). By default it is non-persistent — meaning the cache lives only for the duration of a single PHP request and is discarded the moment the response is sent. Install a persistent object cache drop-in (a file placed at wp-content/object-cache.php) and that same API starts writing to an in-memory store that survives between requests.
Plugins like WooCommerce, Advanced Custom Fields, and Yoast SEO make heavy use of wp_cache_get() and wp_cache_set(). Without persistence, each new visitor triggers the full query chain. With a warm persistent cache, those calls return data from RAM in under a millisecond.
Two caveats worth stating before the numbers:
- Object caching does not replace page caching. A full-page cache (Nginx FastCGI cache, WP Rocket, or similar) serves entire HTML responses and is almost always faster for anonymous traffic. Object caching helps most on logged-in pages, WooCommerce cart/checkout flows, and any page that cannot be fully cached.
- The benefit scales with query volume. A five-page brochure site with twenty queries per load will see modest gains. A membership site or store with 80+ queries per load will see dramatic ones.
Test Environment and Methodology
All tests ran on two identical VPS instances (4 vCPU, 8 GB RAM, NVMe SSD) running Ubuntu 22.04, Nginx 1.24, PHP 8.2-FPM, and MySQL 8.0. WordPress 6.5 with WooCommerce 8.9 was installed on each. The product catalog contained 4,200 simple products, 15 active categories, and 3 active shipping zones — a realistic mid-size store.
Cache configurations tested:
- Baseline: no persistent object cache
- Redis 7.2 via the Redis Object Cache plugin (version 2.5.4) with
WP_REDIS_MAXMEMORYset to 512 MB andmaxmemory-policy allkeys-lru - Memcached 1.6.23 via the W3 Total Cache Memcached object cache module (version 2.7.3), allocated 512 MB
Load was generated with k6 running 50 concurrent virtual users over a 3-minute ramp-up, then sustained for 5 minutes. Each VU cycled through: homepage → category page → single product page → cart add → checkout page. This mix exercises both cacheable and uncacheable paths.
Metrics were captured with Grafana + Prometheus scraping the k6 output and the MySQL slow query log (threshold: 100 ms). TTFB was measured at the Nginx access log level, not from an external probe, to isolate server-side processing time from network variance.
Results: TTFB, Query Count, and Cache Hit Rate
The table below shows median and 95th-percentile TTFB for each page type, averaged across the 5-minute sustained load window.
| Page | Baseline p50 | Baseline p95 | Redis p50 | Redis p95 | Memcached p50 | Memcached p95 |
|---|---|---|---|---|---|---|
| Homepage | 312 ms | 580 ms | 41 ms | 89 ms | 48 ms | 104 ms |
| Category page | 498 ms | 910 ms | 67 ms | 134 ms | 79 ms | 158 ms |
| Single product | 274 ms | 521 ms | 38 ms | 76 ms | 44 ms | 91 ms |
| Cart (logged-in) | 389 ms | 744 ms | 112 ms | 198 ms | 128 ms | 231 ms |
| Checkout | 441 ms | 863 ms | 134 ms | 247 ms | 151 ms | 278 ms |
Database queries per page load (median, warm cache):
| Page | Baseline | Redis | Memcached |
|---|---|---|---|
| Homepage | 84 | 11 | 14 |
| Category page | 131 | 19 | 23 |
| Single product | 76 | 9 | 12 |
| Cart | 97 | 41 | 46 |
| Checkout | 118 | 49 | 54 |
Cache hit rate after 10-minute warm-up period:
| Backend | Hit Rate |
|---|---|
| Redis | 94.2% |
| Memcached | 91.7% |
The before → after story is clear on the category page: baseline p50 TTFB was 498 ms, dropping to 67 ms with Redis — an 87% reduction. Memcached brought it to 79 ms, a respectable 84% reduction but 18% slower than Redis on the same hardware.
Cart and checkout pages show smaller gains because WooCommerce intentionally bypasses object cache for session data and stock-level checks. Those queries must always hit the database, which explains why the query counts remain relatively high even with a warm cache.
Why Redis Edges Out Memcached in This Workload
The performance gap is not large enough to be the sole reason to choose one over the other, but it is consistent across every page type and percentile. Three factors explain it:
Data structure support. Redis natively stores lists, hashes, and sorted sets. The Redis Object Cache plugin (by Till Krüss) uses Redis hashes to group related cache keys under a single top-level key, which means cache invalidation — flushing a post's related data on save — requires fewer round-trips. Memcached stores only flat key-value strings, so the W3 Total Cache implementation must issue multiple individual deletes.
Persistence and replication. Redis supports RDB snapshots and AOF logging. After a server restart, the cache rewarms from disk rather than starting cold. During the test, I triggered a deliberate Redis restart at the 8-minute mark; it recovered to 90%+ hit rate within 40 seconds. Memcached started cold after every restart, taking 3–4 minutes to reach equivalent hit rates under the same load.
Connection handling. Both backends were accessed over a Unix socket to eliminate TCP overhead. Even so, the Redis Object Cache plugin's use of PhpRedis (the C extension) rather than a pure-PHP client adds a measurable latency advantage. Memcached clients in PHP are generally pure-PHP or use the older php-memcached extension, which showed slightly higher per-operation overhead in strace sampling.
When Memcached Is Still a Reasonable Choice
Memcached is not the wrong answer in every scenario. Two situations favor it:
Multi-server horizontal scaling. Memcached was designed from the start for distributed deployments. Its consistent-hashing client libraries are mature and well-tested. If you are running WordPress across three or more application servers and want to share a single cache pool without running Redis Cluster or Redis Sentinel, Memcached's operational simplicity is a real advantage.
Host-imposed constraints. Some managed WordPress hosts expose Memcached but not Redis, or offer Redis only on higher-tier plans. An 84% TTFB reduction from Memcached is still an 84% reduction. If your host gives you Memcached, use it — just understand you are leaving a few percentage points on the table compared to Redis.
Recommended Redis Configuration for WordPress
If you control your server or are on a host that exposes Redis configuration, the following settings produced the results above. Add them to /etc/redis/redis.conf:
maxmemory 512mb
maxmemory-policy allkeys-lru
tcp-backlog 511
unixsocket /var/run/redis/redis.sock
unixsocketperm 770
save ""
Disabling save turns off RDB persistence during the test (the restart-recovery test above used AOF, enabled separately). For production, enable AOF if you want warm restarts:
appendonly yes
appendfsync everysec
In wp-config.php, define the connection before the /* That's all */ line:
define( 'WP_REDIS_PATH', '/var/run/redis/redis.sock' );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_TIMEOUT', 1 );
define( 'WP_REDIS_READ_TIMEOUT', 1 );
define( 'WP_REDIS_MAXTTL', 86400 );
Set WP_REDIS_MAXTTL to 86400 (24 hours) rather than leaving it unlimited. Unlimited TTLs cause stale product data to persist after WooCommerce stock updates if the cache invalidation hook misfires — a subtle bug that is hard to diagnose under load.
Do This First
Before installing any object cache backend, run the Query Monitor plugin (version 3.16+) on your site for 24 hours with real traffic. Export the database query log and count how many unique queries fire per page type. If your busiest page generates fewer than 30 queries, object caching will produce single-digit millisecond improvements — not worth the operational overhead.
If Query Monitor shows 60+ queries on key pages, proceed in this order:
- Install Redis on the server (or confirm your host's Redis tier includes a dedicated instance, not a shared one — shared Redis pools can introduce noisy-neighbor latency that erases the gains shown above).
- Install the Redis Object Cache plugin. Enable it from the plugin's dashboard panel, not just by activating the plugin — activation does not automatically drop the
object-cache.phpfile. - Let the cache warm for 10 minutes under normal traffic, then check the plugin's diagnostics panel for hit rate. A hit rate below 80% after warm-up usually means
WP_REDIS_MAXMEMORYis too low and keys are being evicted before they can be reused. - Measure TTFB before and after using your host's built-in metrics or a tool like
curl -o /dev/null -s -w "%{time_starttransfer}\n"against your key pages. Document the delta. If the improvement is less than 20% on your highest-query pages, investigate whether a full-page cache is a better fit for that traffic pattern.
Object caching is infrastructure, not a setting you toggle and forget. The configuration above is a starting point; your query volume, eviction rate, and traffic mix will determine whether you need to increase memory allocation, tune TTLs per cache group, or add a replica for read scaling. Measure first, tune second, and re-measure after every change.