info

Affiliate Disclosure: This article contains benchmarked tools that may compensate us. We only recommend hardware and software tested in our lab environments for maximum LCP and INP efficiency.

Redis vs LiteSpeed Object Cache for WooCommerce Under Load (2026)

Your catalog page renders in 200 ms until the flash sale starts. Then object-cache keys churn, wp_options balloons, and TTFB climbs toward one second. The usual fix — “just add Redis” — misses the real question: which object-cache stack survives your store’s worst hour? This guide compares the two setups WooCommerce stores actually run: Redis…

calendar_today folder

Your catalog page renders in 200 ms until the flash sale starts. Then object-cache keys churn, wp_options balloons, and TTFB climbs toward one second. The usual fix — “just add Redis” — misses the real question: which object-cache stack survives your store’s worst hour?

This guide compares the two setups WooCommerce stores actually run: Redis (via the phpredis or Predis PHP client) and LiteSpeed’s object cache (which is a front-end to Memcached, LiteSpeed’s LSMCD, or Redis). Same store, same traffic, under load — config, eviction, flush behavior, and a test methodology you can run yourself, because we don’t publish numbers we didn’t measure.

First, a terminology correction that changes everything

“LiteSpeed object cache” is not a cache engine. LiteSpeed Cache for WordPress (LSCWP) does not provide object caching itself — it is a control panel that wires WordPress to an external object-cache backend per the LiteSpeed documentation. You enable the module and pick a backend:

LSCWP Method settingActual backend
RedisA real Redis server (phpredis/Predis connects to it)
MemcachedMemcached — or LiteSpeed’s LSMCD, a drop-in Memcached replacement tuned to feed LSCache ESI blocks and act as the object cache LSMCD product page

So the honest comparison is Redis server vs LSMCD/Memcached server, both triggered from the same LSCWP settings screen. Anyone who tells you “LiteSpeed object cache is faster than Redis” is comparing a control panel to a database. Our existing object cache performance showdown and the Object Cache Pro vs LiteSpeed plugin comparison cover the plugin layer; this article is about the backend layer under WooCommerce load.

What WooCommerce actually puts in the object cache

Before choosing a backend, know the workload. A store under load writes and reads three very different key families:

  • Transients — WooCommerce caches product counts, shipping-zone lookups, coupon metadata, wc_product_children_*, and dashboard stats in transients. Without a persistent object cache, expired or non-expiring transients land in wp_options, and transients set without an expiration are stored as autoloaded options — loaded on every single page request WordPress core ticket #54221. With a persistent object cache, transients move out of wp_options into memory. This is the single biggest WooCommerce win from any object cache.
  • Query cachesWP_Query results, term counts, menu items, and (on high-traffic stores) product lookup data that plugins cache by group. This is the cache-hit workload your hit ratio measures.
  • Sessions and nonces — short-lived, high-churn keys. These determine your eviction behavior more than your headline hit ratio.

Our deep dive on object-cache miss rates in Redis + WooCommerce shows why miss rate, not raw ops, is the metric that moves TTFB — that’s what your load test should measure.

Redis: flexible, shared, and eviction-aware

Redis is a general-purpose in-memory data store. For WordPress object caching you use it as a pure key/value cache, which means two settings dominate behavior under load:

maxmemory + eviction policy. Redis will happily grow until the OS kills it. You must cap it and choose what gets evicted first. For a WordPress cache the community-standard policy is allkeys-lru — evict least-recently-used keys across all databases — and a practical starting size is 256 MB with maxmemory-policy allkeys-lru, sized so hot entries stay resident under load. Two alternatives worth knowing: allkeys-lfu (evict least-frequently-used; better when a few keys dominate) and volatile-lru (evict only keys with a TTL — safer for mixed workloads, but it can evict the exact transients you need). In practice: allkeys-LRU or LFU, with enough headroom that evictions stay near zero on a normal day — the guidance echoed across hosting guides on Redis vs Memcached for WooCommerce.

redis.conf starting point:

maxmemory 256mb
maxmemory-policy allkeys-lru
appendonly no
save ""

appendonly no + save "" = no persistence. For a pure object cache that’s correct: a cold cache after restart is fine, and Redis recovers in minutes by re-populating from the database. If you want warm restarts, the Redis 7.2 high-concurrency setup we tested covers RDB snapshots — but don’t pay the AOF cost for a cache you can afford to lose.

The client layer (phpredis vs Predis). Redis itself is fast; the bottleneck is often PHP↔Redis transport. Drop-in plugins use one of two clients: phpredis (a C extension) or Predis (pure PHP). Benchmarks consistently favor phpredis: roughly 2–5× faster on simple GET/SET due to its C implementation, about 2× in the classic Redis-benchmark mailing-list tests, and up to ~6× in some practical client benchmarks a production-style PHP comparison. If your host lets you install PHP extensions, use phpredis. Predis remains a valid fallback (shared hosting without extension access) — at the cost of measurable TTFB on cache-heavy pages.

Multi-server stores. Redis speaks TCP/IP natively and Redis Cluster/Proxy handle many nodes, so it is the natural choice when two+ app servers must share one cache — no per-server duplication, cache coherence without invalidation storms.

LSMCD under load: fast, local, and ESI-aware

LSMCD (LiteSpeed Memcached) is LiteSpeed’s drop-in Memcached replacement, and its design goal is different: it is tuned to feed LSCache ESI blocks and act as the WordPress object cache. On a single LiteSpeed server it is the lowest-friction stack: one daemon serves both the page-level ESI cache and object-level data, and LSCWP connects to it through the Memcached method setting.

Configuration is done in the plugin, not in Redis config files. From the official LSCWP cache documentation, the settings that matter under load are:

SettingVerified recommendation
MethodMemcached for LSMCD/Memcached, Redis for Redis — the plugin does not auto-detect
Host / PortUnix socket path with port 0 for sockets — sockets are noted as more efficient than TCP
Default Object LifetimeKeep it short (LiteSpeed’s own default is 360 s) to avoid stale data on dynamic stores
Persistent ConnectionON (keeps the backend connection alive between requests) — must match memcached.sess_persistent in PHP ini
Store TransientsTransients use the object cache automatically whenever one is available
Do Not Cache GroupsWith SASL auth, add posts/post_meta to avoid known fatal errors in meta.php (non-SASL setups usually don’t need this)
Cache WP AdminOptional; speeds up the dashboard at the cost of memory

The honest trade-offs versus Redis: LSMCD is memory-keyed and simpler, with excellent single-server throughput and first-party ESI integration; but it lacks Redis’s data structures, persistence, and multi-node ecosystem. If your architecture is one LiteSpeed box, LSMCD is a legitimate, often faster-to-deploy choice. If you scale horizontally, you end up on Redis anyway.

The WooCommerce load scenarios that actually decide this

Scenario 1: Flash sale on a single LiteSpeed server

Peak: hundreds of concurrent checkouts, product pages uncacheable for logged-in customers. Both backends absorb the object-cache reads; the differentiator is uncacheable pages. Cart, checkout, and my-account are dynamic and must bypass page cache entirely — a concern independent of your object cache, covered in our WooCommerce checkout speed & AJAX piece. LSMCD has an ergonomic edge here (ESI & object cache in one daemon); Redis needs only slightly more moving parts. Verdict: either; pick by host support and your comfort with a second daemon.

Scenario 2: Stock-update flush storm

Bulk stock edits, import plugins, and coupon changes fire wp_cache_flush()-style invalidation across transients and query groups. The danger is a full flush thrash: every key rebuilt from the database at once, spiking DB CPU. Mitigation is identical on both backends — avoid whole-cache flushes, purge by group, give maxmemory headroom so eviction doesn’t become an accidental full flush. See cache fragmentation under sustained writes for the Redis-specific failure mode here.

Scenario 3: Horizontal scale (2–10 app servers)

Cart/session data lives in the object cache; if each server has its own LSMCD, a customer bouncing between nodes loses their cart. Shared Redis wins outright — one logical cache, network-accessible, with persistence off. This is also where scaling to 10k products starts to depend on cache coherence, not raw speed.

Decision matrix

Your situationChooseWhy
Single LiteSpeed server, lowest ops overheadLSMCD (Memcached method, Unix socket)One daemon serves ESI + object cache; socket + port 0 avoids TCP overhead; config lives in LSCWP
Any multi-server / shared-cache needRedis (phpredis, TCP)One shared logical cache across nodes; cluster-ready
Disk-free, performance-first, phpredis availableRedis + allkeys-lru + no persistenceFastest client path, eviction protects memory
Shared hosting, no PHP extensionsRedis via Predis (or host-provided cache)Predis works without extensions at some TTFB cost
LiteSpeed Enterprise + heavy ESI usageLSMCDFirst-party ESI integration by design

How to test it (so numbers stay honest)

Both backends are credible — the difference shows up in your store’s data, not in marketing. Run this before switching anything:

  1. Baseline on the current stack. Record: wp_options row count, autoloaded-options size, DB query count per uncached request, and p50/p95 TTFB at origin (bypass page cache). Our WordPress Speed Diagnosis tool automates the header-and-query tracing.
  2. Instrument the cache. For Redis: INFO stats gives keyspace_hits / keyspace_misses — compute hit ratio and watch evicted_keys during your test. For LSMCD: monitor via its stats interface; the LSCWP Object Cache tab reports connection status.
  3. Load test with a ramp, not a flat line. A k6 script with ramping virtual users (e.g. 10 → 300 over 3 minutes, hold 5, spike 5) against mixed endpoints — product page, cart, checkout GET — while a background job performs stock updates. Skip checkout POST (you’re testing the cache, not the payment gateway).
  4. Run both backends on the same store and same data. Switch Method in LSCWP, restart, purge, and repeat. Compare hit ratio at peak, p95 TTFB, DB query rate, and evicted_keys/flush behavior during the stock-update storm. Publish only what you measured — that’s the benchmark methodology this site was built on.
  5. Check the second-order metrics. wp_options autoload shrink (transients evacuated), wrong-cart incidents (topology coherence), and checkout TTFB during the spike — these matter more than raw ops/s.

Bottom line

  • “LiteSpeed vs Redis” is a false fork — LSCWP’s object cache is a front-end; the real choice is LSMCD/Memcached vs Redis as the backend.
  • Single server, LiteSpeed stack: LSMCD via Unix socket is the installation with the fewest moving parts and first-party ESI fit.
  • Any scaling plan, multi-node, or serious WooCommerce load: Redis with phpredis, allkeys-lru, 256 MB+ headroom, persistence off — and a short default TTL so stale transients never outlive their usefulness.
  • Never skip the load test. Eviction policy, flush behavior, and hit ratio are store-specific; the cache category and the WooCommerce category map the failure modes we’ve reproduced at scale, so you know what to look for before your next flash sale.

Appendix: Ready-to-Run k6 Load Script (Redis vs LSMCD)

You can’t compare object-cache backends on marketing numbers — you compare them on your own store under a scripted spike. This script models exactly the workload that stresses an object cache: cacheable browsing (home, shop archive, product), an uncacheable session-bound page (cart), and an optional authenticated wp-login.php session with add-to-cart. It avoids checkout POST deliberately — you’re testing the cache layer, not your payment gateway.

Read this before running:

  • Run only against a staging/QA copy, never production.
  • Replace PRODUCT_IDS (defaults: 12,45,67,89,120) with real product IDs from the store.
  • The authenticated flow (login + add-to-cart) is off by default; enable it with AUTH_TEST=1 and real customer credentials.
  • If your store rewrites products to pretty slugs (/product/slug/), adjust the product stage URL accordingly — the ?p= variant only works when pretty permalinks accept query strings.
// woocommerce-object-cache-load.js
// k6 >= 0.45 — run with: k6 run -e BASE_URL=https://staging.yourstore.com woocommerce-object-cache-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE = __ENV.BASE_URL || 'https://staging.yourstore.com';
const AUTH_TEST = (__ENV.AUTH_TEST || '0') === '1';
const USER = __ENV.WC_USER || 'customer';
const PASS = __ENV.WC_PASS || 'customer-pass';
const PRODUCT_IDS = (__ENV.PRODUCT_IDS || '12,45,67,89,120').split(',').map(Number);

export const options = {
  discardResponseBodies: true,
  scenarios: {
    eviction_storm: {
      executor: 'ramping-vus',
      startVUs: 0,
      gracefulRampDown: '30s',
      stages: [
        { duration: '2m', target: 30 },   // warm-up: steady browsing, cache warming
        { duration: '3m', target: 30 },   // hold: normal-ish store load
        { duration: '2m', target: 150 },  // flash-sale spike: the stage that matters
        { duration: '30s', target: 0 },   // ramp down
      ],
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],
    http_req_duration: [
      { threshold: 'p(95)<500', abortOnFail: true, delayAbortEval: '10s' },
    ],
    'http_req_duration{name:shop_home}': ['p(95)<400'],
    'http_req_duration{name:product}': ['p(95)<350'],
    'http_req_duration{name:cart}': ['p(95)<800'],
  },
};

function get(url, name) {
  const res = http.get(url, { tags: { name } });
  check(res, { [`${name} status 200`]: (r) => r.status === 200 });
  return res;
}

export default function () {
  const pick = PRODUCT_IDS[Math.floor(Math.random() * PRODUCT_IDS.length)];

  // Cacheable pages — exercise the page cache and the query/object caches underneath
  get(`${BASE}/`, 'shop_home');
  sleep(0.3 + Math.random() * 0.7);
  get(`${BASE}/shop/`, 'shop_archive');
  sleep(0.5 + Math.random() * 1.5);
  get(`${BASE}/?p=${pick}`, 'product'); // product/category: replace with /product/{slug} if needed
  sleep(0.5 + Math.random() * 1.5);

  // Uncacheable, session-bound — cart must bypass every cache layer (see the Cloudflare
  // checkout-cache guide); this is where a broken cache stack shows up as wrong carts
  get(`${BASE}/cart/`, 'cart');
  sleep(0.5 + Math.random() * 1);

  // Optional authenticated session (staging only): login + add-to-cart each iteration.
  // k6 keeps a per-VU cookie jar, so the session persists across the loop.
  if (AUTH_TEST) {
    const login = http.post(`${BASE}/wp-login.php`, {
      log: USER,
      pwd: PASS,
      'wp-submit': 'Log In',
      redirect_to: `${BASE}/my-account/`,
      testcookie: '1',
    }, { tags: { name: 'login' } });
    check(login, { 'login redirects': (r) => r.status === 302 || r.status === 200 });

    const atc = http.get(`${BASE}/?add-to-cart=${pick}`, { tags: { name: 'add_to_cart' } });
    check(atc, { 'add-to-cart ok': (r) => r.status === 200 });
  }
}

How to run it (and how to compare the two backends)

# Baseline run on the current backend (page cache ON, as in production)
k6 run -e BASE_URL=https://staging.yourstore.com woocommerce-object-cache-load.js

# Authenticated variant (staging only)
k6 run -e BASE_URL=https://staging.yourstore.com -e AUTH_TEST=1 \
  -e WC_USER=your_customer -e WC_PASS=your_password woocommerce-object-cache-load.js

The comparison protocol: run 1 with LSMCD as the backend (LSCWP → Cache → Object → Method: Memcached, host set to the Unix socket, port 0), purge all caches, run the script. Run 2 — swap Method to Redis (phpredis), purge again, run the identical script. Keep the stages untouched between runs; the only variable is the backend.

What to read from the output

The summary table gives you p(95) for every tagged group (shop_home, shop_archive, product, cart, login) plus the global error rate — the two numbers your thresholds already police. Now add the object-cache metrics to complete the picture: after each run, record redis-cli INFO statskeyspace_hits, keyspace_misses (hit ratio = hits / (hits + misses)) and evicted_keys; for LSMCD use its stats command via the Memcached protocol. The decisive comparisons:

  • Hit ratio at peak (150-VU stage) — a backend that starts missing under load is pushing queries back to MySQL exactly when it shouldn’t.
  • evicted_keys growth during the spike — eviction is a silent full-flush: every evicted transient gets rebuilt from the database, spiking DB CPU. Near-zero at peak is the bar.
  • p95 of cart and login — these are uncacheable by design; if they degrade, the object cache (or origin CPU from misses) is the bottleneck, not the page cache.
  • DB query rate during the storm — enable Query Monitor on staging and watch the query count per request; it should stay flat because the cache is absorbing reads.

Whichever backend keeps p95 flat and the error rate under 1% through the spike — with hits rising and evictions staying near zero — is the right one for your store. Numbers from someone else’s benchmark don’t survive contact with your wp_options table, which is exactly why this script exists: it makes the comparison reproducible on your stack, under your flash-sale profile, before your next one goes live.

engineering

About the Lab & Author

This benchmark and optimization guide was written by the Performance Engineering team at CoreVitalsLab. With over a decade of experience optimizing high-traffic WooCommerce stores and enterprise WordPress deployments, we focus exclusively on data-driven TTFB reduction, caching architecture, and passing Core Web Vitals.