Redis 5 min read

Shopify Ripped Out Redis and Went Back to MySQL. Here's Why That's Not Crazy

Mention traffic spikes to any engineer and watch what happens. Someone says cache. Put Redis in front of it, take load off the database, ship it. It’s less a decision than a reflex at this point. Which makes it interesting that Shopify — a company that absorbs millions of requests per second on Black Friday — went the other direction on its inventory reservation path. They pulled Redis out and left MySQL standing alone.

One thing up front: this isn’t a hot take on a breaking story. It’s a system design case that keeps resurfacing in architecture discussions because the reasoning generalizes so well. So rather than chasing live reactions, this piece digs into the design logic underneath the call.

Inventory Is the Worst Possible Data to Cache

Inventory is a strange beast. A product description or an image URL can lag by a second and nobody dies. Stock counts are different. Sell the last remaining unit to two customers and one of them is getting a refund email within the hour.

Flash sales make it obvious. A hundred pairs of limited-edition sneakers, thirty thousand people hitting the button at the same moment. What you need there isn’t “roughly fast reads.” It’s “exactly 100 successful writes, and not one more.” Caches are excellent at the first thing. The problem in front of you is the second thing.

Inventory reservation isn’t a read-heavy workload at all. It’s read, decide, write — bound into a single atomic unit. It’s a transaction. Bolting a cache onto it means shoving a cache into the exact territory caches were never designed to handle.

The Moment You Add Redis, Consistency Becomes Your Problem

Put a stock counter in Redis and a queue of ugly questions forms behind it.

Redis says 3, MySQL says 5 — which one is the truth? A Redis node dies mid-flight; what happens to the reservations that were in progress? A payment fails and you need to return the unit to stock, but Redis happens to be down at that exact second. Where does that unit go?

Every answer is code you have to write. Sync logic between two stores. Reconciliation batch jobs to hunt down drift. Recovery procedures for failure modes. Tests to prove all of it actually works. And here’s the part that gets waved away: this is a distributed transaction. It is one of the genuinely hard problems in computer science, and you just adopted it to save a fraction of a millisecond.

MySQL already solved this. Lock the row with SELECT ... FOR UPDATE, decrement inside a transaction, commit. Rollback is free. Crash recovery is free. These are guarantees that have been hardened over decades of production abuse. The instant you layer a cache on top, those battle-tested guarantees become homework for your application code.

MySQL Is Much Faster Than You Think

Here’s where the standard objection lands: sure, but Redis is way faster than a database.

True, with a caveat that swallows most of the argument. On a properly indexed primary key lookup, MySQL typically responds in around 1 millisecond. Call Redis 0.2 milliseconds and the gap is under a millisecond. Meanwhile a single web request round-tripping from a browser to your server burns tens to hundreds of milliseconds. On that scale, the difference isn’t a bottleneck. It’s rounding error.

And hot data is already sitting in MySQL’s buffer pool. You’re reading from memory, not spinning disks. The whole “in-memory store versus disk-based store” framing was comparing something that doesn’t exist to something else.

None of this means MySQL always wins. Complex multi-join queries, ephemeral data like sessions and rate limits, genuinely read-dominated paths — caches earn their keep there, no argument. The point is narrower: inventory reservation was not one of those cases.

The Real Bottleneck Usually Isn’t Cacheable

Caching is seductive because it’s the fix that doesn’t require fixing anything. Slow query? Stash the result. The root cause is untouched but the dashboard turns green, and green dashboards end meetings.

Then time passes. While the cache masks that query, nobody has any reason to go optimize it. Data volume grows. The query gets slower in the dark. Now a single cache miss is a small catastrophe. And when traffic surges at the precise moment the cache is cold — a deploy, an eviction, a node restart — every one of those hidden slow queries stampedes the database at once. That’s the thundering herd, and it’s an outage that exists specifically because you added a cache.

Systems at Shopify’s scale survive without caches in these paths not through some proprietary magic. They designed indexes properly, sharded the data, tuned connection pools, and rewrote the queries themselves. That work is boring. It is also far more predictable than debugging a cache invalidation bug at 3 a.m. while the incident channel fills up.

Removing Complexity Is a Scaling Strategy

The lesson here is not “Redis is bad.” That conclusion would just repeat the original mistake in the opposite direction — picking the tool first and reshaping the problem to fit it.

The lesson is about sequence. Every component you add to a system adds a stateful store, a failure point, an operational surface, and one more concept a new engineer has to internalize before they can ship safely. That bill is invisible on day one. It arrives all at once around year two, usually during an incident.

So flip the order. Measure first. Find out what is actually slow, with data, not intuition. Fix indexes, queries, and schema. If it’s still slow after that, then evaluate a cache. Follow that sequence and you end up able to explain why the cache exists — which, notably, most teams cannot do about the caches already running in their stack.

The Takeaway

Shopify’s decision reduces to a single question: is this cache solving a problem you measured, or is it there because everyone does it that way? On paths where correctness outranks latency — inventory, payments, anything involving a number that must never be wrong — removing a component is the better scaling move.

So look at the Redis instance in your own stack. Which one is it? If you can’t immediately say what breaks when you delete it, that uncertainty is the finding.

Redis MySQL System Design Shopify Infrastructure

Comments

    Loading comments...