We replaced Redis with MySQL for inventory reservations—and it scaled (2026) - ShopifySkip to Content
SearchType something you're looking for
Log inStart for free
blog|Infrastructure<br>We replaced Redis with MySQL for inventory reservations—and it scaled<br>How we used SKIP LOCKED, composite primary keys, and connection visibility to hit our scale targets.
Published on May 12, 2026
img]:mt-3 [&_a>img]:mb-8 [&_a>img]:h-[revert-layer] [&_img]:my-12 [&_a>img]:inline-block [&_figure>img]:mb-0 [&_figure]:mb-12 [&_img]:inline-block [&_ul]:list-disc [&_ul]:space-y-3 [&_ul]:pl-6 [&_ul]:mb-6 [&_ul_li]:pl-4 [&_ol]:list-decimal [&_ol]:space-y-3 [&_ol]:pl-6 [&_ol]:mb-6 [&_ol_li]:pl-4 [&_ol_li::marker]:font-bold [&_iframe]:max-w-full [&_figcaption]:mb-12 [&_figcaption]:text-center [&_figcaption]:text-xs [&_figcaption]:sm:text-sm [&_figcaption]:mt-1 [&_strong]:font-medium [&_.heading--4]:font-aktivgroteskextended [&_.heading--4]:text-2xl [&_.heading--4]:font-medium [&_.heading--4]:tracking-[-.02em] [&_iframe]:mt-3 [&_iframe]:mb-9 [&_.container]:m-0 [&_.container]:flex [&_.container]:gap-5 [&_.container]:flex-wrap [&_.green]:text-[green] [&_.red]:text-[red] [&_.pros]:bg-[#eafaea] [&_.pros]:rounded-[10px] [&_.pros]:p-5 [&_.pros_h3]:mt-2 [&_.cons]:bg-[#ffebe6] [&_.cons]:rounded-[10px] [&_.cons]:p-5 [&_.cons_h3]:mt-2 [&_.pros]:tablet:flex-[1_0_calc(50%-10px)] [&_.pros]:flex-[1_0_100%] [&_.cons]:tablet:flex-[1_0_calc(50%-10px)] [&_.cons]:flex-[1_0_100%] [&_.pros_.grid-container]:grid [&_.cons_.grid-container]:grid [&_.grid-container]:gap-[5px] [&_.grid-container]:grid-cols-[15px_auto] [&_.pros_.grid-container]:p-[5px] [&_.cons_.grid-container]:p-[5px] [&_.aspect-video_iframe]:mt-0 article-content [&_.truncated-text]:relative [&_.truncated-text]:max-h-[300px] [&_.truncated-text]:overflow-hidden [&_.truncated-text]:mb-6 [&_.truncated-text::before]:absolute [&_.truncated-text::before]:content-[""] [&_.truncated-text::before]:bottom-0 [&_.truncated-text::before]:w-full [&_.truncated-text::before]:h-[150px] [&_.truncated-text::before]:bg-gradient-to-t [&_.truncated-text::before]:from-white [&_.truncated-text::before]:from-50% [&_.truncated-text::before]:to-transparent [&_.truncated-text\_\_toggle]:absolute [&_.truncated-text\_\_toggle]:bottom-0 [&_.truncated-text\_\_toggle]:w-full [&_.truncated-text\_\_toggle]:text-center [&_.marketing-code]:bg-[#f0f1f2] [&_.marketing-code]:font-bold [&_.marketing-code]:p-1 [&_.marketing-code.marketing-code--block]:p-4 [&_.marketing-code--block]:block [&_.marketing-code--block]:max-w-full [&_.marketing-code--block]:overflow-x-scroll [&_.marketing-code--block]:mb-5 [&_.partners-signup]:mt-6 [&_.partners-signup]:mb-12 [&_.partners-signup]:bg-gray-200 [&_.partners-signup]:p-3 [&_.partners-signup]:md:p-6 [&_.table]:block [&_.table]:overflow-x-scroll [&_.table]:max-w-full text-engineering-dark-text [&_h2]:text-inherit [&_h3]:text-inherit [&_h4]:text-inherit [&_.heading--4]:text-inherit [&_a]:text-[#E7ECFB] [&_strong]:text-engineering-dark-text" itemProp="articleBody"><br>During checkout, when a buyer clicks "Complete purchase," we need to guarantee the items they're buying are still available. If we get this wrong in one direction, two buyers purchase the same last unit: the merchant has to cancel an order, send an apology email, and eat the support cost. If we get it wrong in the other direction, we tell a buyer something is sold out when it isn't, and the merchant loses a sale they should have made.
At Shopify's scale, either failure compounds fast. On Black Friday 2025, merchants on our platform hit a record $5.1 million in sales per minute at peak. Every one of those transactions touches inventory.
Our oversell protection system handles this by reserving inventory during payment processing—a short hold that prevents two concurrent checkouts from claiming the same unit. For years, this ran on Redis. When we moved toward a unified database strategy, we had to answer a hard question: could MySQL handle the same scale?
Earlier attempts had failed. A single row with a quantity column couldn't handle the contention. MySQL 8's SKIP LOCKED feature introduced a different design: one row per inventory unit instead of one row per item. Inspired by 37signals' approach to database-backed load distribution, we rebuilt reservations on MySQL and hit our high-throughput targets during peak 2025 traffic.
But the hardest lesson wasn't about database design. It was discovering that the real bottleneck wasn’t what we were observing and measuring. This post walks through the solution and what we found along the way.
The challenge
What is oversell protection?
Oversell protection has two main operations:
Reserve : When payment starts, we mark items as reserved (a short hold, e.g. several minutes).
Claim : When payment succeeds, we permanently deduct quantity from the inventory ledger (source of truth).
Checkout completion depends on this being fast and correct. Slow reservations trigger throttling and a worse buyer...