<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa" -->

---
title: We replaced Redis with MySQL for inventory...
description: Shopify&#x27;s engineering team replaced Redis with MySQL 8 for inventory reservations, achieving scale targets during Black Friday 2025 peak traffic ($5.1M/min)....
canonical: https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: We replaced Redis with MySQL for inventory reservations—and it scaled (2026) | daily.dev
og:description: Shopify&#x27;s engineering team replaced Redis with MySQL 8 for inventory reservations, achieving scale targets during Black Friday 2025 peak traffic ($5.1M/min)....
og:url: https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa
og:image: https://api.daily.dev/og/posts/7UecbCOOA.png
og:image:alt: We replaced Redis with MySQL for inventory reservations—and it scaled (2026)
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# We replaced Redis with MySQL for inventory reservations—and it scaled (2026)

**[Hacker News](https://daily.dev/sources/hn)** · 11 min read · 1 upvotes · 0 comments

## Summary

Shopify's engineering team replaced Redis with MySQL 8 for inventory reservations, achieving scale targets during Black Friday 2025 peak traffic ($5.1M/min). The core design uses one row per sellable unit with MySQL's SKIP LOCKED to avoid contention, a bounded pool of 1,000 rows per item/location to keep scans fast, and composite primary keys to reduce lock count. Key technical fixes included switching to READ COMMITTED isolation to eliminate gap locks, enforcing consistent lock ordering to prevent deadlocks, and batching with UNION ALL. The real bottleneck turned out to be database connection exhaustion caused by other checkout processes—not the reservation queries themselves. Instrumenting SQL statements with connection tags at the app layer and aggregating at the ProxySQL layer revealed which callers held connections longest. Cleanup of the checkout path removed 50% of reads and 33% of transactions on the primary. During high-volume flash sales, writer CPU stayed under 50% and reader CPU under 16%.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://shopify.engineering/scaling-inventory-reservations>

## Questions this post answers

### How can I use MySQL 8 SKIP LOCKED to build a high-throughput inventory reservation system instead of Redis?

Use a one-row-per-sellable-unit design instead of a single row with a quantity column, since SKIP LOCKED lets concurrent transactions select and lock different available rows without waiting on each other. Cap the pool of available rows per item/location combination (Shopify uses 1,000) and refill it via a replenishment process to keep table scans fast while absorbing traffic bursts.

_Explore how daily.dev surfaces database concurrency patterns for engineers scaling transactional systems._

### Why does an auto-increment primary key cause extra row locks in MySQL InnoDB SELECT FOR UPDATE queries?

An auto-increment primary key forces InnoDB to lock both the secondary index used in the WHERE clause and the clustered index, resulting in two row locks per reservation instead of one. Switching to a composite primary key made of the columns actually filtered on (shop_id, inventory_item_id, inventory_group_id, id) reduces this to a single lock per row, improving throughput under high concurrency.

_Developers tuning InnoDB lock behavior track these schema tradeoffs on daily.dev._

### Why would a database system hit a throughput ceiling with low CPU usage but growing thread queuing?

Low CPU with high queuing often signals connection exhaustion rather than a compute bottleneck. In Shopify's checkout system, tagging SQL statements with business-process comments and measuring connection hold time per caller at the ProxySQL layer revealed that other checkout code was holding connections longer than reservations, depleting the shared pool and creating an artificial ceiling.

_Engineers debugging connection pool limits can find similar diagnostic approaches on daily.dev._

## Community take

How the wider developer community reacted, aggregated from 6 discussions and 110 comments across hackernews (as of 2026-09-13).

**TL;DR:** The community is genuinely interested in the technical approach but heavily distracted by suspicions that the post was AI-written, and a vocal thread debates whether the 1,000-row-per-SKU design is actually the right solution or an over-engineered workaround.

**Sentiment:** 25% positive · 45% mixed · 30% skeptical

**The case for**

- Consolidating reservations into MySQL eliminates a second storage system (Redis), reducing operational complexity.
- The SKIP LOCKED approach spreads row-level contention across many rows, avoiding the single-counter bottleneck that plagued Redis.
- Discovering that connection exhaustion—not the reservation queries—was the real bottleneck is a genuinely useful real-world lesson.
- The linked InnoDB locking deep-dive was praised as a valuable companion resource.

**The pushback**

- Multiple commenters believe the post was largely AI-generated, citing telltale LLM phrasing, an AI image appended at the end, and internal inconsistencies like mismatched table names.
- The 1,000-row bounded pool and its replenishment process are criticized as an unnecessary complexity ('algorithmic smell') when simpler counter-based or cart-row designs might suffice.
- The proposed alternative (one row per cart×SKU) sparked a long unresolved debate about whether it actually prevents overselling under concurrency.
- Shopify's reported culture of mandating AI usage and evaluating employees on AI adoption raised concerns about engineering quality and authenticity of technical output.

**By community**

- hackernews (mixed): Technically curious but split between appreciation for the real-world scaling details and skepticism about AI authorship, the 1,000-row design choice, and Shopify's engineering culture.

**Hottest debate:** Whether the 1,000-row-per-SKU pool with a replenishment process is a justified concurrency optimization or an over-engineered workaround compared to simpler single-row or per-cart designs.

**Open questions**

- Does the replenishment process itself become a bottleneck or failure point under extreme flash-sale load?
- Would a per-cart×SKU row design actually prevent overselling under high concurrency without some form of locking on a shared counter?
- How does the system handle payment methods that don't support separate authorization and capture, which are common globally?
- Was the post actually written by an LLM, and if so, does that undermine the credibility of the engineering claims?

**Highlights**

> I have never worked anywhere where describing how their system actually works would pass the company's own system design interview
> — [atomicnumber3 on hackernews](https://news.ycombinator.com/item?id=49228580)

> > Shopify incorrectly formulated the very problem they are trying to solve. That’s a bold overconfident statement.  Cart abandonment is real. People never clear their carts they just walk away Shopify purposefully chooses to do it at payment time because doing it earlier results in lost sales as people “reserve” items and then walk away causing other to see out of stock and then also walk away Whoever puts up the money first gets the item That’s the design constraint they chose you can’t just say “their solution is wrong because they solved the wrong problem”. Each design is a different user experience and I think it’s safe to say they chose which experience they want consciously.
> — [admax88qqq on hackernews · 1 comments](https://news.ycombinator.com/item?id=49228775)

> > not the best design [...] So those engineers at Shopify worked hard for months on a more performant system, but they missed the obvious structure? They chose a complex denormalization for no good reason? It may be true, but I think it's presumptuous to belittle their work when we have only partial information. My guess is that they had good reasons to think that the more obvious ways would not scale. And from reading your comments in this thread, I believe your structure would fail at their scale. A SQL query that uses 2 sub-queries with "group by" is probably too heavy. From the post, at peaks there would be millions of active shopping carts. BTW, I suspect most orders are just for 1 or 2 of each item, so the denormalization is not as heavy as it seems.
> — [idoubtit on hackernews · 2 comments](https://news.ycombinator.com/item?id=49228943)

> > how does current design resolve concurrent actors fighting for the last item ? It resolves with skip locked. Assuming we have only 1 item left. First query scans the buffer table, locks as many rows as needed (1 in our case), and moves rows to another table. Second query scans the table, finds no rows (even if first one hasn’t finished yet, the row is locked and ignored), checks if it can increase buffer, finds out that it’s fully sold and aborts. Db guarantees that you can’t oversold. > my design is the same, except it does not need this dance with moving rows between tables, locking them, and the cludge with replenishment process. I can’t evaluate whether it’s the same or not, because you still haven’t clarified when exactly you’re going to insert the row. In the article they’re inserting in the same transaction. Would you also do it in the transaction? Because if you’ll introduce a separate global mechanism to resolve conflicts, on a high level it would be the same as their approach with redis (you need to have 2 systems) EDIT: wording
> — [soontimes on hackernews · 1 comments](https://news.ycombinator.com/item?id=49228579)

> It’s fascinating that in order to do this, they had to remove 50% of reads and 33% of transactions from the main DB.
> — [srcreigh on hackernews](https://news.ycombinator.com/item?id=49228083)

**Source threads**

- [hackernews](https://news.ycombinator.com/item?id=49226536) · 160 points · 108 comments
- [hackernews](https://news.ycombinator.com/item?id=48225314) · 4 points · 0 comments
- [hackernews](https://news.ycombinator.com/item?id=48142160) · 4 points · 0 comments
- [hackernews](https://news.ycombinator.com/item?id=48646907) · 3 points · 2 comments
- [hackernews](https://news.ycombinator.com/item?id=48206115) · 1 points · 0 comments
- [hackernews](https://news.ycombinator.com/item?id=48132262) · 1 points · 0 comments

## Similar posts on daily.dev

- [Trying DuckDB for Magento Analytics: An Experiment with One Million Orders](https://daily.dev/posts/trying-duckdb-for-magento-analytics-an-experiment-with-one-million-orders-xunzodi5k) · MariaDB.org · 0 upvotes · 0 comments
- [I Tried the “Just Use Postgres” Queue for Six Weeks. SKIP LOCKED Paid Until It Didn’t](https://daily.dev/posts/i-tried-the-just-use-postgres-queue-for-six-weeks-skip-locked-paid-until-it-didn-t-sni9qhs1s) · Medium · 2 upvotes · 0 comments
- [Shopify powers observability for global-scale commerce with ClickHouse](https://daily.dev/posts/shopify-powers-observability-for-global-scale-commerce-with-clickhouse-r45dmqcus) · ClickHouse · 3 upvotes · 0 comments
- [How we migrated the database behind every Vercel build](https://daily.dev/posts/how-we-migrated-the-database-behind-every-vercel-build-wstvjzabz) · Vercel · 3 upvotes · 2 comments

---

Tags: [#redis](https://daily.dev/tags/redis), [#mysql](https://daily.dev/tags/mysql)

[View this post on daily.dev](https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"We replaced Redis with MySQL for inventory reservations—and it scaled (2026)","url":"https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa"},"datePublished":"2026-08-09T02:04:12.345Z","dateModified":"2026-09-13T20:45:00.691Z","description":"Shopify's engineering team replaced Redis with MySQL 8 for inventory reservations, achieving scale targets during Black Friday 2025 peak traffic ($5.1M/min)....","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/affe117ccebab0a376c2bc58f5f39cb6?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/affe117ccebab0a376c2bc58f5f39cb6?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Hacker News","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Hacker News","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/hn","url":"https://daily.dev/sources/hn"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"redis,mysql","timeRequired":"PT11M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Hacker News","item":"https://daily.dev/sources/hn"},{"@type":"ListItem","position":3,"name":"We replaced Redis with MySQL for inventory reservations—and it scaled (2026)"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/we-replaced-redis-with-mysql-for-inventory-reservations-and-it-scaled-2026--7uecbcooa#faq","mainEntity":[{"@type":"Question","name":"How can I use MySQL 8 SKIP LOCKED to build a high-throughput inventory reservation system instead of Redis?","acceptedAnswer":{"@type":"Answer","text":"Use a one-row-per-sellable-unit design instead of a single row with a quantity column, since SKIP LOCKED lets concurrent transactions select and lock different available rows without waiting on each other. Cap the pool of available rows per item/location combination (Shopify uses 1,000) and refill it via a replenishment process to keep table scans fast while absorbing traffic bursts. Explore how daily.dev surfaces database concurrency patterns for engineers scaling transactional systems."}},{"@type":"Question","name":"Why does an auto-increment primary key cause extra row locks in MySQL InnoDB SELECT FOR UPDATE queries?","acceptedAnswer":{"@type":"Answer","text":"An auto-increment primary key forces InnoDB to lock both the secondary index used in the WHERE clause and the clustered index, resulting in two row locks per reservation instead of one. Switching to a composite primary key made of the columns actually filtered on (shop_id, inventory_item_id, inventory_group_id, id) reduces this to a single lock per row, improving throughput under high concurrency. Developers tuning InnoDB lock behavior track these schema tradeoffs on daily.dev."}},{"@type":"Question","name":"Why would a database system hit a throughput ceiling with low CPU usage but growing thread queuing?","acceptedAnswer":{"@type":"Answer","text":"Low CPU with high queuing often signals connection exhaustion rather than a compute bottleneck. In Shopify's checkout system, tagging SQL statements with business-process comments and measuring connection hold time per caller at the ProxySQL layer revealed that other checkout code was holding connections longer than reservations, depleting the shared pool and creating an artificial ceiling. Engineers debugging connection pool limits can find similar diagnostic approaches on daily.dev."}}]}
```

