首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 汽车 游戏 社会热点 开源推荐 医疗健康 归档 标签 关于

Shopify 用 MySQL 取代了 Redis 来进行库存预订,而且它还可以扩展

摘要

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 un...

the and this one inventory buyer two same unit per
2026-08-09 1 阅读 约5分钟阅读 adletbalzhanov
分享:
字号:
在结账过程中,当买家点击“完成购买”时,我们需要保证他们购买的商品仍然可用。如果我们在一个方向上犯了这个错误,两个买家会购买相同的最后一件商品:商家必须取消订单,发送一封道歉电子邮件,并承担支持成本。如果我们在另一个方向上搞错了,我们会告诉买家某样东西已经售罄,而实际上并没有,而商家则失去了他们本应完成的销售。在 Shopify 的规模下,任何一种失败都会迅速加剧。 2025 年黑色星期五,我们平台上的商家每分钟销售额达到创纪录的 510 万美元。这些交易中的每一笔都涉及库存。我们的超售保护系统通过在付款处理期间保留库存来解决这个问题,这是一种短期保留,可以防止两个并发结账认领同一单位。多年来,它一直在 Redis 上运行。当我们转向统一数据库策略时,我们必须回答一个难题:MySQL 能否处理相同的规模?早期的尝试都失败了。具有数量列的单行无法处理争用。 MySQL 8 的 SKIP LOCKED 功能引入了不同的设计:每个库存单元一行,而不是每个项目一行。受到 37signals 数据库支持的负载分配方法的启发,我们重建了 MySQL 预留,并在 2025 年流量高峰期间达到了高吞吐量目标。但最难的一课并不是数据库设计。它发现真正的瓶颈并不是我们所观察和测量的。这篇文章将介绍该解决方案以及我们在此过程中发现的内容。挑战 什么是超卖保护?超售保护有两个主要操作: 保留:付款开始时,我们将商品标记为保留(短暂保留,例如几分钟)。索赔:付款成功后,我们会从库存分类帐中永久扣除数量(事实来源)。结帐的完成取决于其速度和正确性。缓慢的预订会引发限制和更糟糕的买家体验。错误意味着过度销售(愤怒的客户)或销售不足(收入损失)。规模和正确性要求这里的规模并不是抽象的:Shopify 为美国超过 14% 的电子商务提供了动力,在 2025 年黑色星期五,我们看到每分钟的销售额比上一年峰值增长了 11%。每次涉及库存的结账都会进行预订,因此系统必须在不丢弃请求或破坏一致性的情况下处理突发情况。我们需要: 在高峰流量期间支持平台的高性能吞吐量目标 尊重多地点库存(仅从可以满足的地点进行预订) 在预订和库存分类账之间保持 ACID 保证 优先考虑正确性:不超售,不丢失预订 Redis 模型及其限制 以前的系统将预订存储在 Redis 中。每个项目都有一个数量键,保留表示 DECR ,释放表示 INCR 。 Redis 可以很好地处理并发性,但预订和库存分类账位于两个不同的系统中。索赔步骤(处理付款、永久扣除库存)需要更新 MySQL 和清理 Redis,而这两个操作无法包含在单个原子步骤中。根据订单,这可能会导致超售(商品已售出但从未从分类账中扣除)或卖空(商品已扣除但仍标记为保留)。最重要的是,Redis 模型没有多位置意识,并且增加了维护单独集群的运营成本。将预订转移到与账本相同的 MySQL 数据库中意味着我们可以将所有内容包装在 ACID 事务中,并完全消除这些故障模式。解决方案:SKIP LOCKED 核心思想:每个单位一行,受设计限制 我们不是每个带有数量列的项目一行,而是每个可销售单位一行。包含 10 个单位的项目有 10 行。保留三个单元意味着在单个事务中选择并移动三行。通过将预订和库存分类账保存在同一个数据库中,我们可以在预订和索赔之间获得 ACID - 修复 Redis 可能出现的错误类别(例如,付款成功但库存未索赔,反之亦然)。简化的保留流程如下所示: SKIP LOCKED 使其具有可扩展性:如果另一个事务锁定了某些行,MySQL 会跳过它们并返回其他可用行。无需在同一排等待,减少争用。但是,所有库存的每单位一行将出现大规模故障 - 跨 10 个位置的 50,000 单位的商品意味着 500,000 行,并且储备查询在扫描它们时会变慢。相反,我们维护一个有界的可用行池,每个项目/位置组合的上限为 1,000。预留消耗该池中的行;补货流程会从库存分类账中重新填充它。为什么是 1,000?上限需要足够大,以吸收突发而不至于耗尽,但又足够小,以保持表紧凑和 SKIP LOCKED 扫描快速。我们根据闪购期间观察到的每个商品/地点的峰值预订率来确定其规模:1,000 份
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱