Stack Guides & Tips

Redis for WooCommerce: what it fixes and what it doesn’t

Published · Last updated

The question I get asked is some version of “will Redis make my WooCommerce store faster?” Usually it’s asked after a host has suggested it, or after Site Health has started nagging about a persistent object cache.

The honest answer is that Redis speeds up some of the work WordPress does on every request, and does nothing at all for some of the things that make WooCommerce stores slow. This article covers which is which, the WooCommerce-specific behaviour you should know about, how to pick a plugin, how to measure whether it helped, and the mistakes that quietly undo it.

It doesn’t cover installing and configuring the Redis server itself (persistence, maxmemory, eviction policy, redis.conf). That’s in my Redis configuration guide, and this article assumes you’ll follow it. For the bigger picture of what changes as a store grows, see the WordPress scalability guide.

What a persistent object cache actually does

WordPress already has an object cache. Every time it loads an option, a post, a term or a user, it keeps a copy in PHP memory so the second lookup in the same request doesn’t go back to the database. The catch is that this cache is thrown away at the end of every request, so the next page load starts from nothing.

A persistent object cache keeps those copies between requests. You install a drop-in file at wp-content/object-cache.php, and from then on the wp_cache_*() functions read from and write to Redis instead of a PHP array that dies with the request. Transients move as well: with a persistent object cache active, set_transient() writes to Redis rather than to wp_options.

So the saving is this: lookups that would have been a database query on every page become a Redis read instead. On a store with a lot of options, product meta and terms, that’s a lot of small queries gone.

WordPress itself will tell you when it thinks you need one. Site Health suggests a persistent object cache once a site crosses any of its thresholds, for example more than 1,000 posts, 1,000 users or 500 autoloaded options. Most WooCommerce stores cross the posts threshold on product variations alone.

What it doesn’t do

This is the part the hosting upsell leaves out.

It doesn’t stop WooCommerce generating pages for shoppers with a basket or an account. Most page caches bypass anyone with a basket or a login cookie, so those shoppers get a freshly built page every time: PHP runs, every plugin loads, the theme renders. Redis makes some of the lookups inside that build cheaper. It doesn’t skip the build. If your problem is that your best customers get the slow site, the fix is page caching that works for them, which is what Super Speedy AJAX Prices and the done-for-you Cache Everything for Everyone service are for.

It doesn’t fix a slow query. WordPress caches WP_Query results in the object cache, but the cache key includes a “last changed” stamp for posts, and core bumps that stamp whenever any post or post meta is added, updated or deleted. On a store that’s busy (product edits, imports, and on a store without HPOS, every order) the cached catalogue queries are invalidated constantly, and the next visitor runs the slow SQL again. If the query takes two seconds without Redis, it takes two seconds on every miss with Redis. Slow catalogue, search and filter queries need indexes and better SQL, which is the job of Scalability Pro.

It doesn’t fix a bloated autoload. WordPress loads every autoloaded option on every request, Redis or no Redis. With Redis, that blob comes out of Redis instead of MySQL, but it still has to be transferred and unserialised in full on every page. A few megabytes of autoloaded junk is still a few megabytes. Clean it up first; my guide to managing the wp_options autoload covers how.

It doesn’t make writes cheaper. Orders, stock changes, session saves and scheduled actions are all writes to MySQL. Redis is a cache in front of reads.

WooCommerce-specific behaviour

Sessions still live in the database

WooCommerce keeps a guest’s basket and session data in the wp_woocommerce_sessions table. With a persistent object cache, the session handler also keeps a copy in the object cache (cache group wc_session_id) and checks there first when it loads a session. Every save still writes to the table, then updates the cached copy.

So Redis saves the session read, not the session write. A store with a lot of guest baskets still writes to that table, and still needs WooCommerce’s own cleanup of expired sessions to keep it from growing. My configuration guide covers excluding the wc_session_id group from Redis if you ever need to.

Cart fragments

Since WooCommerce 7.8, the cart fragments script is no longer loaded on every page by default. It loads where the classic Cart widget is used, or where a theme or plugin enqueues it or lists it as a dependency. Where it does run, it makes an Ajax request that loads WordPress for every page view, and page caches don’t cache it.

Redis makes that request cheaper, because the options and session it loads come from Redis. It doesn’t remove the request. If your theme still enqueues cart fragments on every page, removing that request saves far more than caching what it loads.

Transients

WooCommerce caches a lot in transients: variation price ranges, product children, layered nav counts and more. With Redis active, these go into Redis instead of wp_options, which keeps wp_options smaller.

One detail matters for memory. WooCommerce invalidates groups of cached data by changing a version number or prefix that it builds into the key, rather than deleting every old key. The old entries stay in Redis until they expire or get evicted, and the Redis Object Cache plugin writes keys with no expiry unless you set WP_REDIS_MAXTTL. That’s why the eviction policy in the configuration guide has to be an allkeys- policy. A volatile- policy only evicts keys that have an expiry, and Redis’s own documentation says it behaves like noeviction when no keys have one: once memory is full, writes fail.

Product lookup tables

Since WooCommerce 3.6, sorting and filtering by price, stock and rating uses the wc_product_meta_lookup table instead of joining post meta. That table makes the SQL cheaper. Redis doesn’t replace it, and a cached query result is only as good as the last time the posts stamp changed (see above). If the lookup table is out of date, regenerate it from WooCommerce > Status > Tools. Redis won’t paper over it.

Action Scheduler

Action Scheduler, which WooCommerce uses for background jobs, stores its queue in its own tables (actionscheduler_actions, actionscheduler_claims, actionscheduler_groups and actionscheduler_logs). Its database store doesn’t use the object cache at all. A queue of hundreds of thousands of pending or failed actions is a MySQL problem and stays one after Redis goes in.

HPOS

With High-Performance Order Storage, orders live in their own wc_orders tables rather than in wp_posts and wp_postmeta. That has a side effect people miss: on a store with HPOS on and compatibility mode (sync to posts) off, saving an order no longer touches the posts tables, so it no longer invalidates every cached post query on the site.

WooCommerce also has an HPOS Data Caching setting under WooCommerce > Settings > Advanced > Features. It caches raw order data in the object cache, it’s off by default, and WooCommerce’s own description says it’s recommended for stores using object caching. It arrived in WooCommerce 9.6 as an alpha feature. WooCommerce warns that extensions which change order data with direct SQL, rather than through the CRUD layer, can leave stale order data in the cache, so test your order-handling plugins on staging before turning it on.

Choosing a drop-in

Only one object-cache.php drop-in can be active, so you pick one plugin to own it.

  • Redis Object Cache by Till Krüss. Free, 500,000+ active installations, version 3.0.0 at the time of writing. Supports the PhpRedis, Predis and Relay clients and has WP-CLI commands (wp redis status, wp redis enable). This is the one I recommend, and it’s what Kinsta and GridPane install by default. Use PhpRedis rather than Predis if your host offers it; the configuration guide explains why.
  • Object Cache Pro, also by Till Krüss. A paid, closed-source drop-in that requires an active licence to keep working. The vendor lists faster serialisation and compression, prefetching, analytics, and “optimized for WooCommerce” among its features. I haven’t measured it against the free plugin, so I’m not going to tell you it’s faster. Several hosts bundle it (Cloudways did for all customers as of its March 2023 announcement) and GridPane sells licences as an add-on. If your host includes it, use it. If you’re paying for it yourself, check current pricing at objectcache.pro and measure before and after like any other change.
  • LiteSpeed Cache. Its Object tab connects WordPress to an external Redis or Memcached server; it doesn’t provide the cache itself. Reasonable if you’re on LiteSpeed and already use that plugin for page caching.
  • WP Redis by Pantheon. Still maintained for Pantheon sites, around 9,000 installs, and at the time of writing tested only up to WordPress 6.9, two major versions behind. I’d only use it on Pantheon.

If your host doesn’t offer Redis at all, my configuration guide has an alternative for that situation.

Hosting and control panel support

Most good WordPress hosts and control panels now offer Redis. How it’s sold varies a lot:

  • Kinsta sells Redis as an add-on at $100 per site per month. It installs a dedicated 2 GB Redis instance in the site’s container, plus the Redis Object Cache plugin.
  • GridPane has object caching as a toggle in the site’s Caching tab, using the free Redis Object Cache plugin, with Object Cache Pro and the Relay client available as paid add-ons.
  • RunCloud installs Redis from the server’s Services page, then connects WordPress through its own RunCache plugin or the standalone Redis Object Cache plugin.
  • A VPS you manage yourself: install Redis from your distribution’s packages and follow the configuration guide.

Paying $100 a month for something you can run free on your own server is the kind of thing I’d weigh up before choosing a host. My guide to choosing WordPress hosting covers that.

How to tell whether it helped

Measure before you switch it on, and measure the pages that matter. A cached home page served to a logged-out visitor never reaches PHP, so it can’t tell you anything about Redis.

  1. Pick the uncached pages. Product pages and category pages with a basket, the basket page, checkout, My Account, and the wp-admin orders screen. Load each a few times and note the page generation time and query count with Query Monitor.
  2. Enable the object cache, warm it, measure again. The first request after a flush is slower while the cache fills, so ignore it.
  3. Look at the hit rate. Query Monitor’s overview shows the object cache hit rate for the request and flags anything under 75%. That figure counts hits from WordPress’s in-request memory as well as from Redis, so read it as a rough guide. For Redis on its own, redis-cli INFO stats gives keyspace_hits, keyspace_misses and evicted_keys. A low hit rate with a rising evicted_keys means Redis is too small. A low hit rate with no evictions usually means something keeps flushing it.
  4. Compare like for like. Same pages, same basket, same time of day.

If you’d rather not do this by hand, Super Speedy Performance Analysis reports the object cache hit rate for each page it profiles, and its cache impact analysis profiles your slowest pages with the persistent object cache on and off, naming the plugins that ignore the cache.

Common ways it goes wrong

Something keeps flushing the cache. Every wp_cache_flush() empties the lot, and some plugins call it far too often. If your hit rate never builds, the Redis Object Cache FAQ has a short snippet that logs a backtrace on every flush so you can find the culprit. Imports are a common trigger. A Scalability Pro customer found their object cache being flushed constantly while an import ran, and updating a single order in wp-admin slowed to a crawl until the flushing stopped.

Redis runs out of memory. With no maxmemory-policy, Redis refuses new writes when it’s full, and the plugin logs OOM command not allowed. With a volatile- policy and keys that have no expiry, you get the same result. Set maxmemory and an allkeys- policy as the configuration guide describes.

Several sites share one Redis. Give every site its own WP_REDIS_PREFIX, or sites with the same table prefix will read each other’s data. The Redis Object Cache FAQ describes exactly that: a site redirecting to another domain. Know too that the plugin’s flush is a FLUSHDB, which empties the whole Redis database, not just your prefix. If staging and live share database 0, flushing staging empties live’s cache too. Separate databases (or separate Redis instances) keep flushes apart, and the plugin’s FAQ says each site must have its own database and prefix.

The serialiser changed. The plugin can store values with igbinary instead of PHP’s own serialisation, which uses less memory. It needs the igbinary PHP extension. If you switch serialiser, or move a site to a server without the extension, flush the cache afterwards so nothing is read back in the wrong format.

Stale data after an import or a database change. The object cache only knows about changes that go through WordPress and WooCommerce’s own functions. An import tool, a migration or a script that writes to the database with direct SQL bypasses the invalidation, and shoppers see the old price or stock until the entry is evicted. Flush the object cache after a direct database import or a restore, and prefer tools that write through WooCommerce’s CRUD layer or clear the affected entries themselves.

When Redis is the wrong fix

Redis is worth having on almost any WooCommerce store with a real catalogue. It’s the wrong first fix when the time is going somewhere else:

  • Shoppers with baskets get uncached pages. Fix page caching for them.
  • One query takes seconds. Fix the query. Redis only hides it until the next post change.
  • Autoload is huge. Clean it up, or Redis just serves the same bloat from memory.
  • Checkout waits on outbound HTTP calls. Payment gateways, licence checks and cache purges calling remote servers aren’t touched by Redis. On a test store running Nginx Helper with its default settings, one checkout made 30 outbound purge requests; with Scalability Pro’s checkout purge shield on, it made none. That only applies where a purge plugin reacts to order notes, but it’s the kind of thing to look for.
  • Redis is slow to reach. A Redis server on another machine, or an overloaded one, can make a site slower. The plugin’s FAQ says a slower site with Redis means something is broken, and suggests redis-cli --latency-history to check.

If you don’t yet know where your time goes, start with how to diagnose WordPress performance problems before buying anything.

Summary

  • Redis turns repeated database reads into memory reads, across requests. That’s worth having on a WooCommerce store, and it’s cheap if you run your own server.
  • It doesn’t skip page generation for basket holders and logged-in customers, doesn’t fix slow SQL, doesn’t shrink autoload, and doesn’t make writes cheaper.
  • WooCommerce sessions, Action Scheduler and the product lookup tables all still live in MySQL. HPOS takes orders out of the posts tables and adds an optional order data cache.
  • Use the free Redis Object Cache plugin unless your host bundles Object Cache Pro. Give each site its own prefix and database.
  • Measure uncached pages before and after, watch the hit rate and evictions, and find whatever keeps flushing the cache.
  • Configure the server properly: the Redis configuration guide covers persistence, memory and eviction.

Comments

▲
▼
1/1