מבוא לבחירת דאטה בייס – חלק ב׳: מודדים במקום לנחש
קוראי הבלוג עם זיכרון ארוך אולי זוכרים שבאוגוסט 2022 הבטחתי פוסט המשך. בעיה מהעולם האמיתי, כמה בסיסי נתונים, השוואת עלויות וביצועים. אחר כך עברו ארבע שנים, היה אקזיט, היה שקט, וההבטחה נשארה בסוף הפוסט כמו TODO בקומנט שאף אחד לא מוחק.
אז הנה המספרים. בלי Redis is the new king, ובלי להמליץ לכם להתחתן עם אף אחד.
תקציר הפרקים הקודמים
בחלק א׳ דיברתי על עקרונות: קודם כותבים מה קוראים, אחר כך מחליטים איך שומרים, ודוחים את החתונה עם בסיס הנתונים כמה שאפשר. הסיפור מהחיים היה משחק תורות מרובה משתתפים. ארבעה שחקנים, השרת מציג שאלה, ברגע שמישהו עונה לכולם יש 15 שניות, כולם עושים polling ל־REST כדי לראות מה חדש על הלוח.
הממשק נראה בערך ככה:
|
1 2 3 4 5 6 7 8 9 10 11 |
def get_updates(last_index): # תחזיר את עדכוני הלוח מאז last_index pass def make_move(move_type, args): # מהלך של שחקן pass def update_board(new_row): # השרת מוסיף שורה ללוג אחרי כל מהלך pass |
הניסיון הראשון היה Redis, כי רצינו משהו מהיר שחי בין שרתים. זה היה איטי ולא סקלבילי. אחר כך Firebase Realtime Database, כי דחיפה לקליינט נשמעת כמו הפתרון הנכון ל־polling. גם זה לא הציל. בסוף הבנו שבמשחק עם ארבעה אנשים לכמה דקות אין שום סיבה לבסיס נתונים משותף לכל השרתים, ושמרנו הכל ב־RAM. הביצועים קפצו, ואם שרת נופל באמצע משחק לוקח 30 שניות להרים חדש ואין מה לשחזר anyway.
החוק היה: אל תתחתן עם בסיס הנתונים שלך. מה לא היה שם: מספרים.
מה בעצם מדדנו
אותו ממשק, חמישה מימושים, והעומס שהרג את Redis בסיפור: הרבה קליינטים ששואלים get_updates כל רבע שנייה, ורק מדי פעם מישהו באמת עושה מהלך.
- RAM. מילון בפייתון, לכל משחק רשימת אירועים בזיכרון. הניצחון מ־2022.
- Redis נאיבי. מפתח אחד לכל משחק, GET לכל ה־JSON, מוסיפים שורה, SET חזרה. Redis כמחסן משותף, לא כמבנה נתונים.
- Redis כמו שצריך. רשימה לכל משחק,
LRANGEמ־last_index,RPUSHבמהלך. - SQLite. טבלה על דיסק,
SELECTמ־idx,INSERTבמהלך. WAL. - Postgres. אותה טבלה, על דיסק, עם fsync. הדבר שאנשים מתחתנים איתו.
Firebase לא רץ בחי. אין חשבון ניסוי, ולא רציתי לשלם כדי להוכיח נקודה. את העלות מחשבים ממחירון ברגע שיש נפח polling.
שתי רמות עומס: 50 משחקים (200 שחקנים) ו־200 משחקים (800 שחקנים). Poll כל 250ms, מהלך כל 3 שניות, חימום שתי שניות, מדידה 20. בלי HTTP. Redis על localhost בלי persistence. Postgres מקומי עם ברירות המחדל. מכונה אחת. מספיק בשביל סדרי גודל, לא בשביל DBA.
המספרים
50 משחקים, p99 של get_updates:
| מימוש | p50 | p95 | p99 | קריאות לשנייה |
|---|---|---|---|---|
| RAM | 0.001ms | 0.004ms | 0.009ms | 800 |
| Redis כמו שצריך | 0.32ms | 4.0ms | 10ms | 800 |
| SQLite | 0.46ms | 5.5ms | 9.2ms | 800 |
| Redis נאיבי | 4.1ms | 16ms | 25ms | 800 |
| Postgres | 13ms | 25ms | 37ms | 800 |
אפס שגיאות. כבר פה רואים שרשימה ב־Redis זה לא אותו דבר כמו JSON שלם בכל GET.
200 משחקים, 800 שחקנים:
| מימוש | p50 | p95 | p99 | קריאות לשנייה |
|---|---|---|---|---|
| RAM | 0.001ms | 0.002ms | 0.005ms | 3,200 |
| Redis כמו שצריך | 0.45ms | 9.6ms | 33ms | 3,200 |
| SQLite | 14ms | 34ms | 47ms | 3,200 |
| Redis נאיבי | 9.7ms | 43ms | 81ms | 3,200 |
| Postgres | 50ms | 136ms | 200ms | 3,200 |

get_updates, 200 משחקים. הציר ליניארי בכוונה: RAM בקושי נראה, וזה הסיפור.RAM נשאר על אלפית המילישנייה. Redis שנכתב כמו שצריך יורד מ־81ms ל־33ms ב־p99, ועדיין לא קרוב ל־RAM. SQLite על דיסק מנצח את ה־Redis הנאיבי, ומנצח את Postgres בעומס הזה (יותר קוראים ממספר החיבורים במאגר, ו־Postgres משלם fsync). make_move ב־Postgres באותו עומס: p99 של 328ms. במשחק עם חלון של 15 שניות זה עוד חי. בשרת שגם עושה דברים אחרים זה כבר רעש.

RAM מנצח כי אין רשת, אין JSON בקריאה, ואין תהליך אחר. לא פייר מול בסיס נתונים, וזה בדיוק העניין: למשחק תורות עם ארבעה אנשים, בסיס נתונים הוא פיצ'ר ששילמתם עליו בלי להזמין אותו.
כמה זה עולה
מחירון ציבורי מאוגוסט 2026, לא חשבונית.
RAM ו־SQLite. אפס מעל השרת שכבר רץ.
Redis מנוהל. ElastiCache cache.t3.micro, כ־12 דולר לחודש גם כשהצומת מובטל. בין אם כתבתם רשימה ובין אם כתבתם JSON, החשבון אותו דבר. הביצועים לא.
Postgres מנוהל. Cloud SQL מתחיל בסביבות 9 דולר לחודש למכונה קטנה. אותו סדר גודל כמו Redis, על עומס שהקובץ המקומי פתר בלי חשבון.
Firebase Realtime Database. אחסון 5 דולר לג׳יגה אחרי הג׳יגה הראשון, הורדות דולר לג׳יגה אחרי בערך 10 ג׳יגה בחודש. REST polling של 800 שחקנים הופך את ההורדות לבור. Push אמיתי זול יותר, וב־2022 הוא לא הציל אותנו כי הבעיה הייתה המודל.
הפתרון הזול ביותר היה לא לקנות בסיס נתונים.
"אם אין לך בעיית סקייל, אל תקנה פתרון סקייל. תקנה קפה."
מתוך מדריך ההייטקיסט לגלקסיה, פרק שלא עבר code review
מה לקחת מזה
מכונה אחת, בלי HTTP, Redis בלי דיסק, Postgres עם fsync ברירת מחדל. מי שיגיד מזה ש־Postgres איטי או ש־Redis מהיר, לא קרא.
מה כן: העומס פה הוא אלפי get_updates ריקים, לא המהלכים. אל תתחתן עם בסיס הנתונים שלך, ואל תמדוד אותו לפי הברושור. תמדוד את הקריאה שקורית 50 פעם יותר מהכתיבה.
הערה: הטיוטה נכתבה עם Grok בממשק Grok Bot ב־Cursor. המדידות הן ניסוי מקומי בפייתון 3.13 מול Redis 8.0.2, SQLite 3.46 ו־Postgres 17. המספרים מהקוד, לא מהמודל.

כתיבת תגובה