The reading room · Digna Legi
Shopify replaced Redis with MySQL for inventory reservations–and it scaled
/100
80–100: high value. 70–79: worth the time. Below 70: below the usual publication threshold.
Historical score; not verified under the current evidence-review process.
Scores reflect one reader’s profile, not an objective quality rating. Best is a separate personal selection.
How scoring works →This brief · about 3 min with detail
Why read this
High-throughput correctness came from changing the data shape, then discovering the binding constraint was connection ownership, not query speed.
AI brief · Checked against source text
The main idea
Shopify argues that inventory reservation could move from Redis to MySQL once the workload was modeled as many lockable unit rows, not one contested quantity row. MySQL 8’s SKIP LOCKED let concurrent checkouts take different available rows without waiting on the same row, while keeping reservations and the inventory ledger inside one ACID transaction. The surprising second claim is diagnostic: after query and lock tuning, the real ceiling came from other checkout code holding scarce database connections too long.
Some background helpful. Basic understanding of databases, transactions, locks, and high-throughput checkout systems.
Go a little deeper
Correctness improves by collapsing systems
The Redis version handled concurrent decrements, but reservation and inventory truth lived in different places. The failure mode was not Redis speed; it was the lack of one atomic operation spanning reservation cleanup and ledger deduction. Moving both into MySQL let reserve and claim participate in the same transaction, eliminating classes of oversell and undersell bugs created by split-brain state.
The row model turns contention into selection
A single quantity row makes every buyer fight over the same record. Shopify’s alternative creates one row per sellable unit, so reserving three units means locking and moving three available rows. SKIP LOCKED matters because locked rows are bypassed rather than waited on, converting checkout contention into finding other available units. The bounded pool prevents this model from exploding into huge scan-heavy tables.
Database details became throughput controls
The migration only worked after low-level lock behavior was treated as product-critical. An auto-increment key caused InnoDB to take extra locks through both secondary and clustered indexes; a composite primary key cut that down. MySQL’s default REPEATABLE READ created gap-lock problems during replenishment, so reservations used READ COMMITTED. These are not cosmetic optimizations; they determine whether concurrency collapses under load.
Connection time exposed the real shared resource
Reservation latency and CPU looked acceptable, yet throughput still hit a ceiling. The useful measurement was not simply which query was slow, but which business process held a connection and for how long. By tagging SQL at the application layer and aggregating hold time at ProxySQL, Shopify found that other checkout paths were consuming connection capacity, making reservations appear guilty because they arrived at an already depleted pool.
A case from the article
Shadow mode cutover
Shopify did not migrate by abruptly declaring MySQL authoritative. It wrote every reservation to both Redis and MySQL while Redis remained the source of truth, then compared real production behavior for correctness and performance. Because Redis kept a complete live view, rollback remained possible through a kill switch, and rollout could proceed pod by pod from lower-traffic merchants to the highest-volume ones.
How the case is made
The case is made through an engineering migration narrative with production traffic, load-test observations, lock inspection, rollout strategy, and specific database changes.
Where the idea has limits
The design depends on a bounded available-unit pool, replenishment behavior, and careful InnoDB tuning; it is not a blanket claim that replacing Redis with MySQL is automatically safe for every coordination workload.
A question to take away · from Digna Legi
Where are you measuring slow queries when you should be measuring who holds shared capacity longest?
What the original adds
The source gives implementation details beyond the high-level lesson: composite primary keys to reduce row locks, READ COMMITTED to avoid gap-lock deadlocks, UNION ALL batching, and app-to-proxy connection attribution.
About this brief
AI-written, then separately checked for source support, useful detail and clarity. The author’s claims and our editorial question are kept separate. The original remains the author’s work. How we select and summarise →
How was this brief?
Rate this summary, separately from the author’s article.
Optional. Saved in this browser; shared only if you allow analytics.
How was the original article?
Rate the author’s original after reading it.
Optional. Saved in this browser; shared only if you allow analytics.
Digna legi. Worth reading.