שופיפיי העבירה הזמנות מלאי ל-MySQL
שופיפיי העבירה את מערכת הזמנות המלאי שלה מ-Redis למסד הנתונים MySQL שכבר החזיק את ספר חשבונות המלאי.
שופיפיי העבירה את מערכת הזמנות המלאי שלה מ-Redis למסד הנתונים MySQL שכבר החזיק את ספר חשבונות המלאי. מאגר מוגבל של שורות יחידה הניתנות לנעילה בנפרד מאפשר לקופות מקבילות להשתמש ב-SKIP LOCKED כדי לבחור יחידות זכאיות שונות. התכנון תלוי גם בגבולות טרנזקציות, פריסת מפתח ראשי, כללי מילוי מחדש ותצפית על זמן החזקת חיבור בקופה.
קראו את המהדורה הכתובה (אנגלית) ↗
מה מכסה הסרטון הזה
- מודל מונה הכמויות הישן של Redis טיפל בקונקורנטיות, אך ניקוי הזמנות ועדכון ספר חשבונות ה-MySQL לא יכלו לשתף טרנזקציה אטומית מקומית אחת.
- החלופה משתמשת בשורה אחת לכל יחידה זמינה במאגר מוגבל ל-1,000 יחידות לשילוב פריט/מיקום. מאגר ריק יכול להפעיל מילוי מחדש מוטבע עם בקשות מקבילות הממתינות מאחורי נעילת מילוי מחדש.
- המפתח הראשי המורכב הוא (shop_id, inventory_item_id, inventory_group_id, id). שופיפיי הפחיתה את תקורה הנעילה באב הטיפוס שלה והשתמשה ב-READ COMMITTED כדי להימנע מנעילות פער שחסמו מילוי מחדש.
- הזמנות מוחקות שורות מאגר לפני הוספת רשומות הזמנה. ביצוע (Commit) משחרר נעילות מסד נתונים בעוד ההחזקה המאוחסנת נשמרת לאורך התשלום; תשלום מוצלח תובע את ספר החשבונות ומסיר את ההזמנה בטרנזקציה אטומית מאוחרת יותר.
- תיעוד MySQL מתאר את SKIP LOCKED כתצוגה לא עקבית שמשמיטה שורות נעולות. היא אינה קובעת ספירת מלאי מלאה או תור הוגן, חלה רק על נעילות ברמת השורה ואינה בטוחה עבור שכפול מבוסס הצהרה.
- הדוגמה המקורית כוללת expires_at, אך שופיפיי אינה מתעדת את אלגוריתם הניקוי של תפוגת MySQL. ענף השחרור/תפוגה של הסרטון הוא דרישת מחזור חיים הסברתית.
- זמן החזקת החיבור בקוד קופה אחר היה צוואר הבקבוק הסופי של התפוקה. שופיפיי כתבה צל את שתי המערכות, השוותה תוצאות ועברה בהדרגה עם מנגנון כיבוי (kill-switch) חזרה ל-Redis.
תמליל מתורגם
תורגם מהקריינות המקורית באנגלית. זמינות אודיו וכתוביות נשלטת על ידי YouTube.
למה להעביר הזמנות ל-MySQL?
0:00 קופה זקוקה ל-Redis כדי להישאר מהירה. שופיפיי העבירה הזמנות מלאי ל-MySQL באמצעות שורה אחת לכל יחידה ב מאגר מוגבל. למה לבחור ב-MySQL? איך מדלגים על נעילות בבטחה? זה The Daily Diff, מתחת למכסה המנוע. ולמה שאילתות מהירות עדיין הגיעו לתקרה? שופיפיי היא פלטפורמת מסחר למכירה מקוונת ובאופן אישי. Redis הוא מאגר נתונים בזיכרון.
0:21 MySQL הוא מסד נתונים יחסי, ושופיפיי כבר שמרה את ספר חשבונות המלאי שלה שם. הזמנה מחזיקה בקצרה מלאי בזמן שקונה משלם.
למה שתי חנויות היו מסוכנות?
0:29 מודל ה-Redis שלהם הפחית מונה פריטים. תביעת הזמנה ששולמה משמעותה עדכון ספר חשבונות ה-MySQL וניקוי Redis. כתיבות נפרדות אלו יכלו להשאיר מלאי שנמכר פעמיים או לא זמין כשהוא צריך להיות ניתן למכירה. המודל הישן גם חסר מודעות למיקום. החלופה חייבת לבחור מלאי ממקום שיכול למלא את ההזמנה. מחסן ביבשת הלא נכונה יוצר רישום מצוין במסד הנתונים ו- הבטחת משלוח איומה. ניסיונות MySQL קודמים השתמשו בשורת כמות, כך שקופות מתחרות עמדו בתור ב-
מה הופך ליחידה הניתנת לנעילה?
0:56 אותה נעילה. חשבו על חבל קטיפה סביב תא בגיליון אלקטרוני. הוספת עובדים נוספים רק מאריכה את התור. שופיפיי שינתה את האובייקט הניתן לנעילה. כל יחידה זמינה מקבלת שורה משלה. קריאה נועלת מדלגת על יחידות שטרנזקציה אחרת מחזיקה ובוחרת יחידות זכאיות אחרות. עובדים שונים יכולים לרכוש שורות שונות, בזמן שהמונה החם נשאר מחוץ לדרכם.
מה קורה כשהמאגר מתרוקן?
1:15 מאגר זה מוגבל לאלף שורות לכל פריט ומיקום. מילוי מחדש שואב מספר החשבונות. אם הוא מתרוקן, נתיב ההזמנה ממלא מחדש באופן מוטבע, כאשר בקשות מתחרות ממתינות מאחורי נעילת מילוי מחדש. מאגר ריק אינו אומר מחסן ריק. הזמנה מוחקת שורות מאגר נבחרות, ואז מכניסה רשומות הזמנה ב- טרנזקציה. ביצוע (Commit) משחרר את נעילות מסד הנתונים.
1:35 ביטול (Rollback) מבטל את השינויים. ההזמנה שורדת את עיבוד התשלום כמצב מאוחסן. תשלום מוצלח תובע את ספר החשבונות ומסיר את ההזמנה באופן אטומי. נעילות מסד נתונים לעולם אינן צריכות "לשמור" על טופס התשלום. המפתח הראשי המורכב שלהם מתחיל בחנות, פריט, קבוצה, ואז זהות יחידה. התאמת החיפוש הפחיתה נעילת אינדקס באב הטיפוס שלהם. הם גם משתמשים ב-READ COMMITTED כדי למנוע את נעילות הפער שחסמו את מילוי
1:57 מחדש של המאגר, וסדר טבלאות עקבי כדי למנוע המתנות מעגליות. הדוגמה שפורסמה רושמת זמן תפוגה. תשלומים נטושים זקוקים בסופו של דבר לשחרור מלאי, או שעגלת הקניות הופכת לבעל בית. הפוסט של שופיפיי משאיר את אלגוריתם הניקוי הזה ללא פירוט, אז דיאגרמה זו מראה את דרישת מחזור החיים. הנה המלכוד.
מה משמיט SKIP LOCKED?
2:14 Skip locked לא כולל שורות נעולות, ולכן המדריך מכנה את התוצאה שלו תצוגה לא עקבית. הוא אינו מספק ספירת מלאי מלאה וגם לא תור הוגן. שמרו את החלטת הזמינות וכללי המילוי מחדש סביבה.
איפה הייתה התקרה האמיתית?
2:26 ומה עם התקרה הזו? קוד קופה אחר החזיק חיבורי מסד נתונים יותר מדי זמן. שופיפיי תייגה קוראים ומדדה זמן החזקת חיבור, ואז ניקתה את נתיב הקופה ובדקה מחדש קונקורנטיות תהליכונים. שאילתות מהירות עדיין יכולות לעמוד בתור מחוץ לשאילתה.
למה אשלח את התכנון הזה?
2:38 הם כתבו בצל את שתי המערכות כאשר Redis סמכותי, השוו תוצאות, ואז עברו בהדרגה עם מנגנון כיבוי (kill switch). פסק הדין שלי הוא SHIP IT. הייתי שולח את גבול הטרנזקציה המשותף ואת ההשקה הניתנת לביטול, עם כל נתיב הקופה ממוכשר. יש לך שאלה על זה? שים אותה בתגובות. וזה ה-diff להיום.
2:54 אני ניקו מ-Axrisi. מזג באחריות.
מקורות
- 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



