<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>shopify on </title>
    <link>/tags/shopify/</link>
    <description>Recent content in shopify on </description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sat, 19 Sep 2026 11:16:47 +0800</lastBuildDate><atom:link href="/tags/shopify/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Shopify 把库存预留换回 MySQL，以及 flash sale 防超卖的另外五种做法</title>
      <link>/posts/shopify-inventory-reservations-mysql/</link>
      <pubDate>Sat, 19 Sep 2026 11:16:47 +0800</pubDate>
      
      <guid>/posts/shopify-inventory-reservations-mysql/</guid>
      <description>2016 年 2 月，Kylie Cosmetics 在 Shopify 上做了一次 drop，把自己的店和同一个 database shard 上其他所有店一起打挂了。Shopify 后来在工程博客里复盘：每开一次 checkout 就在 MySQL 里建一条记录，流程往下走的每一步又都回头改这同一条记录。库存系统那次没上场，但并发压在同一行上把库打挂这个失效模式，后来在库存问题上又出现了一次，那是本篇的起点。
起点是 Shopify 2026 年 5 月的工程博客，他们把库存预留从 Redis 换回了 MySQL。写这篇时我把同一个防超卖问题在近几年公开材料里的其他做法也查了一遍，能数出来的至少六种：Shopify 的行池加 SKIP LOCKED、AliSQL 的引擎层语句排队、应用层的库存分桶、按速率放行的等候室、DynamoDB 的条件写重试、Durable Objects 的单线程单写者。这六种之间的差别主要来自各自的业务背景。同样叫 flash sale，形态至少有三种：几百万商家各自开卖、单 SKU 零点同步开闸、长尾 SKU 偶尔撞一次车，三种的争抢密度差距在两三个数量级，能用的方案跟着分岔。这篇文章按六种做法逐一展开，最后用一节把选择条件收拢在一起。
几百万商家各自开卖：Shopify 把 quantity 切成离散行 Shopify 工程博客 2026 年 5 月记录了这次替换，参照的量级是 2025 年黑五峰值每分钟 510 万美元的成交额。这一节分四段看：Redis 为什么出局、新模型长什么样、把队摆在 SQL 层要付的代价、shadow mode 双写怎么切过去。
先交代一下业务侧的链路。顾客走到 checkout 这一步时，系统对涉及的商品执行一次 reserve：把对应数量从可售池里划出来临时占住，防止同一件货被多个买家同时锁定。这笔占位是短命的，原文给的量级是几分钟。付款成功后再执行 claim，把 reserved 的数量从 inventory ledger（库存台账）里真正扣掉，交易才算完成。付款失败或者弃单时，占位被释放，数量回到可售池。
Redis 的 quantity key 进不了 ledger 的事务 原文把范围限定在这两个操作上。预留怎么释放、怎么过期、付款失败或者弃单时那批货怎么回到可售状态，全篇都没有交代。想照着这套设计落地的话，这几条要自己补。</description>
    </item>
    
  </channel>
</rss>
