Shopify hat Bestandsreservierungen in MySQL verschoben
Shopify hat sein Bestandsreservierungssystem von Redis in die MySQL-Datenbank verschoben, die bereits das Bestandsbuch enthielt.
Shopify hat sein Bestandsreservierungssystem von Redis in die MySQL-Datenbank verschoben, die bereits das Bestandsbuch enthielt. Ein begrenzter Pool von einzeln sperrbaren Einheitenzeilen ermöglicht es gleichzeitigen Kassiervorgängen, SKIP LOCKED zu verwenden, um verschiedene geeignete Einheiten auszuwählen. Das Design hängt auch von Transaktionsgrenzen, dem Layout des Primärschlüssels, Nachschubregeln und der Beobachtung der Verbindungsdauer über den Kassiervorgang hinweg ab.
Die schriftliche Ausgabe lesen (Englisch) ↗
Was dieses Video behandelt
- Das alte Redis-Mengen-Zähler-Modell bewältigte die Parallelität, aber die Reservierungsbereinigung und die Aktualisierung des MySQL-Ledgers konnten keine einzige lokale atomare Transaktion gemeinsam nutzen.
- Der Ersatz verwendet eine Zeile pro verfügbarer Einheit in einem Pool, der auf 1.000 pro Artikel-/Standortkombination begrenzt ist. Ein leerer Pool kann eine Inline-Nachschubversorgung auslösen, wobei gleichzeitige Anfragen hinter einer Nachschubsperre warten.
- Der zusammengesetzte Primärschlüssel ist (shop_id, inventory_item_id, inventory_group_id, id). Shopify reduzierte den Sperraufwand in seinem Prototyp und verwendete READ COMMITTED, um Lückensperren zu vermeiden, die den Nachschub blockierten.
- Reservierungen löschen Poolzeilen, bevor Reservierungsdatensätze eingefügt werden. Das Commit freigibt Datenbanksperren, während die gespeicherte Haltezeit über die Zahlung hinaus bestehen bleibt; eine erfolgreiche Zahlung bucht das Ledger und entfernt die Reservierung in einer späteren atomaren Transaktion.
- MySQL dokumentiert SKIP LOCKED als eine inkonsistente Ansicht, die gesperrte Zeilen weglässt. Es stellt weder eine vollständige Bestandszählung noch eine faire Reihenfolge sicher, gilt nur für Zeilenebenen-Sperren und ist für die anweisungsbasierte Replikation unsicher.
- Das Quellbeispiel enthält expires_at, aber Shopify dokumentiert den MySQL-Ablauf-Bereinigungsalgorithmus nicht. Der Release/Expiry-Zweig des Videos ist eine erklärende Lebenszyklusanforderung.
- Die Verbindungsdauer in anderem Checkout-Code war der letzte Durchsatz-Engpass. Shopify hat beide Systeme im Schatten geschrieben, die Ergebnisse verglichen und schrittweise mit einem Redis-Kill-Switch-Fallback umgestellt.
Übersetztes Transkript
Aus der englischen Originalerzählung übersetzt. Verfügbare Audio- und Untertitel werden von YouTube gesteuert.
Warum Reservierungen in MySQL verschieben?
0:00 Ein Checkout muss schnell bleiben, wofür Redis benötigt wird. Shopify verschob Bestandsreservierungen in MySQL, indem es eine Zeile pro Einheit in einem begrenzten Pool verwendet. Warum MySQL wählen? Wie überspringt man Sperren sicher? Das ist The Daily Diff, unter der Haube. Und warum stießen schnelle Abfragen immer noch an eine Grenze? Shopify ist eine Handelsplattform für den Online- und persönlichen Verkauf. Redis ist ein In-Memory-Datenspeicher.
0:21 MySQL ist eine relationale Datenbank, und Shopify führte bereits sein Bestandsbuch dort. Eine Reservierung hält kurzzeitig den Bestand, während ein Käufer bezahlt.
Warum waren zwei Speicher riskant?
0:29 Ihr Redis-Modell dekrementierte einen Artikelzähler. Die Beanspruchung einer bezahlten Bestellung bedeutete die Aktualisierung des MySQL-Ledgers und die Bereinigung von Redis. Diese separaten Schreibvorgänge konnten dazu führen, dass der Bestand zweimal verkauft oder nicht verfügbar war, obwohl er verkäuflich sein sollte. Das alte Modell hatte auch keine Standortkenntnisse. Der Ersatz muss den Bestand von einem Ort auswählen, der die Bestellung erfüllen kann. Ein Lager auf dem falschen Kontinent ist ein hervorragender Datenbankeintrag und eine schreckliche Lieferzusage. Frühere MySQL-Versuche verwendeten eine Mengen-Zeile, sodass konkurrierende Kassiervorgänge an derselben
Was wird die sperrbare Einheit?
0:56 Sperre warteten. Stellen Sie sich ein Samtseil um eine Tabellenzelle vor. Das Hinzufügen weiterer Mitarbeiter verlängert nur die Warteschlange. Shopify änderte das sperrbare Objekt. Jede verfügbare Einheit erhält ihre eigene Zeile. Ein sperrender Lesevorgang überspringt Einheiten, die eine andere Transaktion hält, und wählt andere geeignete Einheiten aus. Verschiedene Mitarbeiter können verschiedene Zeilen erwerben, während der Hot Counter ihnen nicht in die Quere kommt.
Was passiert, wenn der Pool leer ist?
1:15 Dieser Pool ist auf tausend Zeilen pro Artikel und Standort begrenzt. Der Nachschub erfolgt aus dem Ledger. Wenn er leer ist, füllt der Reservepfad inline auf, wobei konkurrierende Anfragen hinter einer Nachschubsperre warten. Ein leerer Pool bedeutet kein leeres Lager. Reserve löscht ausgewählte Poolzeilen und fügt dann Reservierungsdatensätze in einer Transaktion ein. Das Commit gibt die Datenbanksperren frei.
1:35 Ein Rollback macht die Änderungen rückgängig. Die Reservierung überlebt die Zahlungsabwicklung als gespeicherter Zustand. Eine erfolgreiche Zahlung bucht das Ledger und entfernt die Reservierung atomar. Datenbanksperren müssen niemals das Zahlungsformular überwachen. Ihr zusammengesetzter Primärschlüssel beginnt mit Shop, Artikel, Gruppe, dann Einheitsidentität. Das Anpassen der Suche reduzierte die Indexsperrung in ihrem Prototyp. Sie verwenden auch Read Committed, um die Lückensperren zu vermeiden, die die Pool-
1:57 Auffüllung blockierten, und eine konsistente Tabellenreihenfolge, um zirkuläre Wartezeiten zu verhindern. Das veröffentlichte Beispiel enthält eine Ablaufzeit. Abgebrochene Zahlungen müssen irgendwann den Bestand freigeben, oder der Warenkorb wird zu einem Vermieter. Der Beitrag von Shopify lässt den Bereinigungsalgorithmus unbestimmt, daher zeigt dieses Diagramm die Lebenszyklusanforderung. Hier ist der Haken.
Was lässt SKIP LOCKED aus?
2:14 Skip locked schließt gesperrte Zeilen aus, daher nennt das Handbuch sein Ergebnis eine inkonsistente Ansicht. Es bietet weder eine vollständige Bestandszählung noch eine faire Reihenfolge. Halten Sie die Verfügbarkeitsentscheidung und die Nachschubregeln um sie herum ein.
Wo war die eigentliche Obergrenze?
2:26 Und diese Obergrenze? Anderer Checkout-Code hielt Datenbankverbindungen zu lange. Shopify markierte Anrufer und maß die Verbindungsdauer, bereinigte dann den Checkout-Pfad und überprüfte die Thread-Parallelität erneut. Schnelle Abfragen können immer noch außerhalb der Abfrage warten.
Warum sollte ich dieses Design versenden?
2:38 Sie haben beide Systeme im Schatten mit Redis als autoritativer Quelle geschrieben, die Ergebnisse verglichen und dann schrittweise mit einem Kill-Switch umgestellt. Mein Urteil ist: SHIP IT. Ich würde die gemeinsame Transaktionsgrenze und dieses rückgängig machbare Rollout versenden, wobei der gesamte Checkout-Pfad instrumentiert ist. Haben Sie eine Frage dazu? Stellen Sie sie in den Kommentaren. Und das ist der Unterschied für heute.
2:54 Ich bin Niko von Axrisi. Verantwortungsbewusst zusammenführen.
Quellen
- We replaced Redis with MySQL for inventory reservations—and it scaledShopify Engineering — Emilie Noel
- Simplified reservation SQL embedded in Shopify's articleShopify Engineering / CourtneySymons on GitHub Gist
- MySQL 8.0 — Locking ReadsOracle / MySQL Reference Manual
- MySQL 8.0 — Transaction Isolation LevelsOracle / MySQL Reference Manual
- What Is Shopify and How Does It Work?Shopify
- Redis quick startsRedis documentation
- What is MySQL?Oracle / MySQL Reference Manual



